From 18fa942aa50f797e0f9b7c8f9045f09dba204302 Mon Sep 17 00:00:00 2001 From: sdaqo Date: Thu, 30 May 2024 23:43:22 +0200 Subject: [PATCH 1/2] add deeplx --- README.md | 5 +- resources/translator.ui | 53 ++- src/app/channel/mod.rs | 95 ++-- src/app/ctxmenu/imp.rs | 20 +- src/app/ctxmenu/mod.rs | 2 +- src/app/mod.rs | 29 +- src/app/reference_dialog/mod.rs | 10 +- src/app/reference_dialog/translator/imp.rs | 13 +- src/app/reference_dialog/translator/mod.rs | 269 +++++++---- src/app/window/imp.rs | 5 +- src/app/window/mod.rs | 5 +- src/config.rs | 9 +- src/language/mod.rs | 66 +-- src/language/translators/deeplx.rs | 162 +++++++ .../translators/google/googleapiv1.rs | 196 ++++---- .../translators/google/googleapiv2.rs | 79 ++-- .../translators/google/googlescrape.rs | 135 +++--- src/language/translators/google/mod.rs | 441 ++++++------------ src/language/translators/meta.rs | 108 +++++ src/language/translators/mod.rs | 4 +- src/main.rs | 19 +- src/mpv.rs | 103 ++-- 22 files changed, 1050 insertions(+), 778 deletions(-) create mode 100644 src/language/translators/deeplx.rs create mode 100644 src/language/translators/meta.rs diff --git a/README.md b/README.md index 3df8806..4805d51 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,7 @@ cargo build --release ``` ## Features -- Full custom translator widget +- Full custom translator widget (Google, DeeplX) - Auto translation in seperate line - Always works: Open mpv after the Popout or before, it detects mpv and attaches itself - Custom Font and Font size @@ -139,7 +139,8 @@ cargo build --release - Custom text color - Linux & Windows Support -**Note that some features like docked or always on top do not work under wayland!** +**Note 1: Some features like docked or always on top do not work under wayland!** +**Note 2: When setting the api key for the /v2/translate endpoint for DeeplX do it like this (with space) `[yourAccessToken] [yourAuthKey]`** ### Planned - Dictionary for looking up words diff --git a/resources/translator.ui b/resources/translator.ui index d42d7f3..8e614c4 100644 --- a/resources/translator.ui +++ b/resources/translator.ui @@ -8,6 +8,11 @@ gtk-go-forward 5 + + True + False + gtk-redo + True False @@ -145,10 +150,11 @@ + Get Api Key Here True False + False 5 - Get Api Key Here True @@ -170,6 +176,47 @@ 2 + + + True + False + 10 + 10 + 5 + + + True + True + 10 + Translator URL + + + True + True + 1 + + + + + True + True + True + image2 + True + + + False + True + 2 + + + + + True + True + 3 + + True @@ -184,7 +231,7 @@ False True - 3 + 4 @@ -294,7 +341,7 @@ True True - 4 + 5 diff --git a/src/app/channel/mod.rs b/src/app/channel/mod.rs index 8aa4bed..203c4c6 100644 --- a/src/app/channel/mod.rs +++ b/src/app/channel/mod.rs @@ -1,7 +1,7 @@ -use glib::{MainContext, Sender, clone}; -use gtk::{glib, subclass::prelude::ObjectSubclassIsExt, prelude::*}; -use std::thread; +use glib::{clone, MainContext, Sender}; +use gtk::{glib, prelude::*, subclass::prelude::ObjectSubclassIsExt}; use std::panic; +use std::thread; use crate::app::MpvSubsWindow; use crate::mpv::mpv_subs_update; @@ -11,62 +11,65 @@ pub enum Message { UpdateTlLabel(String), SetTlLabelVisibilty(bool), SpawnThread, - Quit + Quit, } pub fn setup_channel(window: &MpvSubsWindow) -> Sender { let (sender, receiver) = MainContext::channel::(glib::PRIORITY_DEFAULT); - receiver.attach(None, clone!(@weak window, @strong sender => @default-return glib::Continue(true), move |msg| { - match msg { - Message::UpdateLabel(text) => { - window.imp() - .sub_label - .get() - .unwrap() - .set_text(text.as_str()); - }, + receiver.attach( + None, + clone!(@weak window, @strong sender => @default-return glib::Continue(true), move |msg| { + match msg { + Message::UpdateLabel(text) => { + window.imp() + .sub_label + .get() + .unwrap() + .set_text(text.as_str()); + }, - Message::UpdateTlLabel(text) => { - window.imp() - .tl_label - .get() - .unwrap() - .set_text(text.as_str()) - }, + Message::UpdateTlLabel(text) => { + window.imp() + .tl_label + .get() + .unwrap() + .set_text(text.as_str()) + }, - Message::SetTlLabelVisibilty(visible) => { - let label_box = window.imp().label_box.get().unwrap(); - let contains_tl_label = label_box - .children() - .len() > 1; + Message::SetTlLabelVisibilty(visible) => { + let label_box = window.imp().label_box.get().unwrap(); + let contains_tl_label = label_box + .children() + .len() > 1; - if visible { - if !contains_tl_label { - label_box.add(window.imp().tl_label.get().unwrap()); - label_box.show_all(); + if visible { + if !contains_tl_label { + label_box.add(window.imp().tl_label.get().unwrap()); + label_box.show_all(); + } + } else if contains_tl_label { + label_box.remove(window.imp().tl_label.get().unwrap()); + label_box.show_all(); } - } else if contains_tl_label { - label_box.remove(window.imp().tl_label.get().unwrap()); - label_box.show_all(); - } - }, + }, - Message::SpawnThread => { - thread::spawn(clone!(@strong sender => move || { - panic::catch_unwind(panic::AssertUnwindSafe(|| { - mpv_subs_update(sender.clone()); - })).ok(); + Message::SpawnThread => { + thread::spawn(clone!(@strong sender => move || { + panic::catch_unwind(panic::AssertUnwindSafe(|| { + mpv_subs_update(sender.clone()); + })).ok(); - sender.send(Message::SpawnThread).ok(); + sender.send(Message::SpawnThread).ok(); - })); - }, - Message::Quit => { window.quit() } - } - glib::Continue(true) - })); + })); + }, + Message::Quit => { window.quit() } + } + glib::Continue(true) + }), + ); sender } diff --git a/src/app/ctxmenu/imp.rs b/src/app/ctxmenu/imp.rs index 0f7c00a..4ee8333 100644 --- a/src/app/ctxmenu/imp.rs +++ b/src/app/ctxmenu/imp.rs @@ -1,12 +1,10 @@ - -use gtk::prelude::*; use gtk::gdk; +use gtk::prelude::*; pub struct ContextMenu { - menu: gtk::Menu + menu: gtk::Menu, } - impl ContextMenu { pub fn new() -> Self { let menu = gtk::Menu::new(); @@ -15,9 +13,10 @@ impl ContextMenu { } pub fn add_item>( - &self, widget: &W, + &self, + widget: &W, callback: Box Inhibit>, - callback_should_show: Option bool>> + callback_should_show: Option bool>>, ) { let item = gtk::MenuItem::new(); item.add(widget); @@ -34,11 +33,9 @@ impl ContextMenu { } }); } - + let widget_clone = widget.clone(); - item.connect_button_press_event(move |_wg, ev| { - callback(&widget_clone, ev) - }); + item.connect_button_press_event(move |_wg, ev| callback(&widget_clone, ev)); self.menu.append(&item); item.show_all(); @@ -46,7 +43,7 @@ impl ContextMenu { pub fn attach_to_widget>(&self, widget: &W) { let cloned_menu = self.menu.clone(); - + widget.connect_button_press_event(move |_, event| { if event.button() == gdk::BUTTON_SECONDARY { cloned_menu.popup_easy(event.button(), event.time()); @@ -59,6 +56,5 @@ impl ContextMenu { Inhibit(false) } }); - } } diff --git a/src/app/ctxmenu/mod.rs b/src/app/ctxmenu/mod.rs index c093eaf..28f5766 100644 --- a/src/app/ctxmenu/mod.rs +++ b/src/app/ctxmenu/mod.rs @@ -101,7 +101,7 @@ pub fn build_ctxmenu(window: &MpvSubsWindow) -> ContextMenu { let sizelock_btn = CheckButton::builder() .label("Lock Size") - .active(config.size_lock != None) + .active(config.size_lock.is_some()) .build(); ctxmenu.add_item( diff --git a/src/app/mod.rs b/src/app/mod.rs index 0af6698..286f7ce 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -1,17 +1,16 @@ -pub mod window; -pub mod ctxmenu; -pub mod utils; pub mod channel; +pub mod ctxmenu; pub mod reference_dialog; +pub mod utils; +pub mod window; -use gtk::{prelude::*, subclass::prelude::ObjectSubclassIsExt, Label}; use gtk::gdk; +use gtk::{prelude::*, subclass::prelude::ObjectSubclassIsExt, Label}; -use window::MpvSubsWindow; -use ctxmenu::build_ctxmenu; -use channel::setup_channel; use crate::config::AppConfig; - +use channel::setup_channel; +use ctxmenu::build_ctxmenu; +use window::MpvSubsWindow; pub fn build_window(app: >k::Application) -> MpvSubsWindow { let window = MpvSubsWindow::new(app); @@ -36,17 +35,11 @@ pub fn build_window(app: >k::Application) -> MpvSubsWindow { label_box.set_homogeneous(true); label_box.set_margin(9); - let sub_label = Label::builder() - .name("sub_label") - .selectable(true) - .build(); + let sub_label = Label::builder().name("sub_label").selectable(true).build(); sub_label.style_context().add_class("sub_label"); ctx_menu.attach_to_widget(&sub_label); - - let tl_label = Label::builder() - .name("tl_label") - .build(); + let tl_label = Label::builder().name("tl_label").build(); tl_label.style_context().add_class("sub_label"); ctx_menu.attach_to_widget(&tl_label); @@ -64,12 +57,10 @@ pub fn build_window(app: >k::Application) -> MpvSubsWindow { label_box.add(window.imp().tl_label.get().unwrap()); } - window.add(&label_box); window.imp().label_box.set(label_box).ok(); window.imp().channel_sender.set(setup_channel(&window)).ok(); - + window } - diff --git a/src/app/reference_dialog/mod.rs b/src/app/reference_dialog/mod.rs index c860590..8d329a0 100644 --- a/src/app/reference_dialog/mod.rs +++ b/src/app/reference_dialog/mod.rs @@ -1,6 +1,6 @@ mod translator; -use gtk::{prelude::*}; +use gtk::prelude::*; use translator::TranslatorWidget; pub struct ReferenceDialog { @@ -20,13 +20,13 @@ impl ReferenceDialog { stack.add_named(&translator, "translator"); // stack.add_named(&dict, "dictionary"); - + dialog.set_title("Reference"); dialog.set_default_size(500, 400); dialog.set_modal(true); dialog.set_transient_for(Some(window)); dialog.set_icon_name(Some("gtk-find-and-replace")); - + let ca = dialog.content_area(); ca.set_vexpand(true); ca.set_hexpand(true); @@ -42,7 +42,7 @@ impl ReferenceDialog { } } - pub fn run_async(&self) { - self.dialog.run_future(); + pub async fn run_async(&self) { + self.dialog.run_future().await; } } diff --git a/src/app/reference_dialog/translator/imp.rs b/src/app/reference_dialog/translator/imp.rs index 337e75f..57efdd4 100644 --- a/src/app/reference_dialog/translator/imp.rs +++ b/src/app/reference_dialog/translator/imp.rs @@ -1,7 +1,7 @@ use glib::subclass::InitializingObject; use gtk::prelude::*; use gtk::subclass::prelude::*; -use gtk::{CompositeTemplate, glib}; +use gtk::{glib, CompositeTemplate}; // https://gtk-rs.org/gtk3-rs/stable/latest/docs/gtk3_macros/derive.CompositeTemplate.html @@ -10,7 +10,7 @@ use gtk::{CompositeTemplate, glib}; pub struct TranslatorWidget { #[template_child(id = "tl_engine_combo")] pub tl_engine_combo: TemplateChild, - + #[template_child(id = "tl_engine_default_cb")] pub tl_engine_default_cb: TemplateChild, @@ -19,7 +19,13 @@ pub struct TranslatorWidget { #[template_child(id = "api_key_hint_label")] pub api_key_hint_label: TemplateChild, - + + #[template_child(id = "url_field")] + pub url_field: TemplateChild, + + #[template_child(id = "reset_url_button")] + pub reset_url_button: TemplateChild, + #[template_child(id = "lang_from_combo")] pub lang_from_combo: TemplateChild, @@ -51,7 +57,6 @@ pub struct TranslatorWidget { pub lang_to_field: TemplateChild, } - #[glib::object_subclass] impl ObjectSubclass for TranslatorWidget { const NAME: &'static str = "TranslatorWidget"; diff --git a/src/app/reference_dialog/translator/mod.rs b/src/app/reference_dialog/translator/mod.rs index 0e02f6f..4b26935 100644 --- a/src/app/reference_dialog/translator/mod.rs +++ b/src/app/reference_dialog/translator/mod.rs @@ -1,33 +1,32 @@ mod imp; -use gtk::{glib, subclass::prelude::ObjectSubclassIsExt}; use glib::clone; use gtk::prelude::*; +use gtk::{glib, subclass::prelude::ObjectSubclassIsExt}; use open; -use crate::language::{prelude::*, translators::google}; use crate::config::{AppConfig, TlEngineConfig}; +use crate::language::{get_tranlators_codes, prelude::*, translators::meta::MetaTranslator}; -glib::wrapper! { +glib::wrapper! { pub struct TranslatorWidget(ObjectSubclass) @extends gtk::Box, gtk::Widget, @implements gtk::Buildable; } - impl Default for TranslatorWidget { fn default() -> Self { - Self::new() + Self::new() } } impl TranslatorWidget { pub fn new() -> Self { - let obj: TranslatorWidget = glib::Object::builder().build(); - obj.load_translators(); - obj.bind_callbacks(); - obj.load_from_config(); - obj + let obj: TranslatorWidget = glib::Object::builder().build(); + obj.load_translators(); + obj.bind_callbacks(); + obj.load_from_config(); + obj } pub fn set_text_and_tl(&self, text: &str) { @@ -35,61 +34,47 @@ impl TranslatorWidget { self.translate(); } - fn load_translators(&self) { - self.imp().tl_engine_combo.append(Some("google_api_v1"), &google::GoogleApiV1::get_name()); - self.imp().tl_engine_combo.append(Some("google_api_v2"), &google::GoogleApiV2::get_name()); - self.imp().tl_engine_combo.append(Some("google_scrape"), &google::GoogleScrape::get_name()); + for tler in get_tranlators_codes() { + self.imp().tl_engine_combo.append( + Some(tler), + &MetaTranslator::new(tler.to_string(), None).get_name(), + ); + } } fn load_stuff_for_translator(&self) { let tl_engine_id = self.imp().tl_engine_combo.active_id().unwrap_or_default(); - - let (lang_iter, key_get_link) = match tl_engine_id.as_str() { - "google_api_v1" => { - (google::Language::get_iterator(), google::GoogleApiV1::get_api_key_url()) - }, - "google_api_v2" => { - (google::Language::get_iterator(), google::GoogleApiV2::get_api_key_url()) - }, - "google_scrape" => { - (google::Language::get_iterator(), google::GoogleScrape::get_api_key_url()) - }, - _ => { return; } - }; + let transltor = MetaTranslator::new(tl_engine_id.to_string(), None); self.imp().lang_from_combo.remove_all(); self.imp().lang_to_combo.remove_all(); - - for lang in lang_iter { - self.imp().lang_from_combo.append( - Some(&lang.to_language_code()), &lang.to_language_name() - ); - self.imp().lang_to_combo.append( - Some(&lang.to_language_code()), &lang.to_language_name() - ); + + for (full, code) in transltor.get_language_map().into_iter() { + self.imp().lang_from_combo.append(Some(&code), &full); + self.imp().lang_to_combo.append(Some(&code), &full); } - if let Some(api_key_url) = key_get_link { + if let Some(api_key_url) = transltor.get_api_key_url() { self.imp().api_key_hint_label.set_label("Get Api Key Here"); self.imp().api_key_hint_label.set_uri(&api_key_url); self.imp().api_key_field.set_sensitive(true); } else { - self.imp().api_key_hint_label.set_label("No Api Key Required"); + self.imp() + .api_key_hint_label + .set_label("No Api Key Required"); self.imp().api_key_hint_label.set_uri(""); self.imp().api_key_field.set_sensitive(false); } + self.imp().url_field.buffer().set_text(&transltor.get_url()); let mut cfg = AppConfig::new(); - let tl_cfg = cfg.translators - .iter() - .find(|t| t.name == tl_engine_id); + let tl_cfg = cfg.translators.iter().find(|t| t.name == tl_engine_id); if let Some(tl_cfg) = tl_cfg { self.load_from_tl_cfg(tl_cfg); } else { - // Create the config! :) And then call load_from_tl_config (this also means all the creations of the configs are unnecaccary :))))) let new_tl_cfg = TlEngineConfig { name: tl_engine_id.to_string(), ..TlEngineConfig::default() @@ -99,60 +84,109 @@ impl TranslatorWidget { cfg.save(); } - self.imp().tl_engine_default_cb.set_active(cfg.default_tl_engine == tl_engine_id); + self.imp() + .tl_engine_default_cb + .set_active(cfg.default_tl_engine == tl_engine_id); self.show_all(); } fn load_from_config(&self) { let config = AppConfig::new(); - self.imp().tl_engine_combo.set_active_id(Some(&config.default_tl_engine)); + self.imp() + .tl_engine_combo + .set_active_id(Some(&config.default_tl_engine)); self.load_stuff_for_translator(); } fn load_from_tl_cfg(&self, tl_cfg: &TlEngineConfig) { self.imp().api_key_field.buffer().set_text(&tl_cfg.api_key); - self.imp().lang_from_combo.set_active_id(Some(&tl_cfg.default_lang_from)); - self.imp().lang_to_combo.set_active_id(Some(&tl_cfg.default_lang_to)); + if let Some(override_url) = &tl_cfg.url { + self.imp().url_field.buffer().set_text(override_url); + } + self.imp() + .lang_from_combo + .set_active_id(Some(&tl_cfg.default_lang_from)); + self.imp() + .lang_to_combo + .set_active_id(Some(&tl_cfg.default_lang_to)); self.imp().lang_from_default_cb.set_active(true); self.imp().lang_to_default_cb.set_active(true); } fn bind_callbacks(&self) { let self_clone: TranslatorWidget = self.clone(); - self.imp().tl_engine_combo.connect_changed(clone!(@weak self_clone => move |wg| self_clone.change_tl_engine(wg))); - self.imp().tl_engine_default_cb.connect_toggled(clone!(@weak self_clone => move |wg| self_clone.default_tl_engine(wg))); - self.imp().api_key_field.connect_changed(clone!(@weak self_clone => move|wg| self_clone.change_api_key(wg))); - self.imp().lang_from_default_cb.connect_toggled(clone!(@weak self_clone => move |wg| self_clone.default_lang_from(wg))); - self.imp().lang_to_default_cb.connect_toggled(clone!(@weak self_clone => move |wg| self_clone.default_lang_to(wg))); - self.imp().lang_from_combo.connect_changed(clone!(@weak self_clone => move |wg| self_clone.change_lang_from(wg))); - self.imp().lang_to_combo.connect_changed(clone!(@weak self_clone => move |wg| self_clone.change_lang_to(wg))); - self.imp().switch_langs_btn.connect_clicked(clone!(@weak self_clone => move |wg| self_clone.switch_langs(wg))); - self.imp().translate_btn.connect_clicked(clone!(@weak self_clone => move |_wg| self_clone.translate(); ())); - self.imp().dict_tab_btn.connect_clicked(clone!(@weak self_clone => move |wg| self_clone.change_to_dict(wg))); - self.imp().api_key_hint_label.connect_clicked(clone!(@weak self_clone => move |wg| self_clone.open_api_get_page(wg))); + self.imp() + .tl_engine_combo + .connect_changed(clone!(@weak self_clone => move |wg| self_clone.change_tl_engine(wg))); + self.imp().tl_engine_default_cb.connect_toggled( + clone!(@weak self_clone => move |wg| self_clone.default_tl_engine(wg)), + ); + self.imp() + .api_key_field + .connect_changed(clone!(@weak self_clone => move|wg| self_clone.change_api_key(wg))); + self.imp() + .url_field + .connect_changed(clone!(@weak self_clone => move|wg| self_clone.change_url(wg))); + self.imp() + .reset_url_button + .connect_clicked(clone!(@weak self_clone => move |wg| self_clone.reset_url(wg))); + self.imp().lang_from_default_cb.connect_toggled( + clone!(@weak self_clone => move |wg| self_clone.default_lang_from(wg)), + ); + self.imp() + .lang_to_default_cb + .connect_toggled(clone!(@weak self_clone => move |wg| self_clone.default_lang_to(wg))); + self.imp() + .lang_from_combo + .connect_changed(clone!(@weak self_clone => move |wg| self_clone.change_lang_from(wg))); + self.imp() + .lang_to_combo + .connect_changed(clone!(@weak self_clone => move |wg| self_clone.change_lang_to(wg))); + self.imp() + .switch_langs_btn + .connect_clicked(clone!(@weak self_clone => move |wg| self_clone.switch_langs(wg))); + self.imp() + .translate_btn + .connect_clicked(clone!(@weak self_clone => move |_wg| self_clone.translate(); ())); + self.imp() + .dict_tab_btn + .connect_clicked(clone!(@weak self_clone => move |wg| self_clone.change_to_dict(wg))); + self.imp().api_key_hint_label.connect_clicked( + clone!(@weak self_clone => move |wg| self_clone.open_api_get_page(wg)), + ); } fn change_lang_from(&self, wg: >k::ComboBoxText) { let cfg = AppConfig::new(); - let lang_id = if let Some(id) = wg.active_id() { id } else { return; }; - + let lang_id = if let Some(id) = wg.active_id() { + id + } else { + return; + }; if let Some(id) = self.imp().tl_engine_combo.active_id() { if let Some(tl_cfg) = cfg.translators.iter().find(|t| t.name == id) { - self.imp().lang_from_default_cb.set_active(tl_cfg.default_lang_from == lang_id); + self.imp() + .lang_from_default_cb + .set_active(tl_cfg.default_lang_from == lang_id); } } } - - fn change_lang_to(&self, wg: >k::ComboBoxText) { - let cfg = AppConfig::new(); - let lang_id = if let Some(id) = wg.active_id() { id } else { return; }; + fn change_lang_to(&self, wg: >k::ComboBoxText) { + let cfg = AppConfig::new(); + let lang_id = if let Some(id) = wg.active_id() { + id + } else { + return; + }; if let Some(id) = self.imp().tl_engine_combo.active_id() { if let Some(tl_cfg) = cfg.translators.iter().find(|t| t.name == id) { - self.imp().lang_to_default_cb.set_active(tl_cfg.default_lang_to == lang_id); + self.imp() + .lang_to_default_cb + .set_active(tl_cfg.default_lang_to == lang_id); } } } @@ -171,7 +205,7 @@ impl TranslatorWidget { cfg.default_tl_engine = id.to_string(); cfg.save(); } - } + } fn change_api_key(&self, wg: >k::Entry) { let mut cfg = AppConfig::new(); @@ -192,13 +226,41 @@ impl TranslatorWidget { } } + fn change_url(&self, wg: >k::Entry) { + let mut cfg = AppConfig::new(); + if let Some(id) = self.imp().tl_engine_combo.active_id() { + if let Some(tl_cfg) = cfg.translators.iter_mut().find(|t| t.name == id) { + tl_cfg.url = Some(wg.buffer().text().to_string()); + cfg.save(); + } + } + } + + fn reset_url(&self, _wg: >k::Button) { + if let Some(tler_id) = self.imp().tl_engine_combo.active_id() { + self.imp() + .url_field + .buffer() + .set_text(&MetaTranslator::new(tler_id.to_string(), None).get_url()); + self.change_url(&self.imp().url_field); + } + } + fn default_lang_from(&self, wg: >k::CheckButton) { if !wg.is_active() { return; } - let lang_id = if let Some(id ) = self.imp().lang_from_combo.active_id() { id } else { return; }; - let tl_engine = if let Some(id) = self.imp().tl_engine_combo.active_id() { id } else { return }; + let lang_id = if let Some(id) = self.imp().lang_from_combo.active_id() { + id + } else { + return; + }; + let tl_engine = if let Some(id) = self.imp().tl_engine_combo.active_id() { + id + } else { + return; + }; let mut cfg = AppConfig::new(); if let Some(tl_cfg) = cfg.translators.iter_mut().find(|t| t.name == tl_engine) { @@ -212,9 +274,16 @@ impl TranslatorWidget { return; } - let lang_id = if let Some(id ) = self.imp().lang_to_combo.active_id() { id } else { return; }; - let tl_engine = if let Some(id) = self.imp().tl_engine_combo.active_id() { id } else { return }; - + let lang_id = if let Some(id) = self.imp().lang_to_combo.active_id() { + id + } else { + return; + }; + let tl_engine = if let Some(id) = self.imp().tl_engine_combo.active_id() { + id + } else { + return; + }; let mut cfg = AppConfig::new(); @@ -227,10 +296,18 @@ impl TranslatorWidget { fn switch_langs(&self, _wg: >k::Button) { if let Some(to_id) = self.imp().lang_to_combo.active_id() { if let Some(from_id) = self.imp().lang_from_combo.active_id() { - self.imp().lang_from_combo.set_active_id(Some(to_id.as_str())); - self.imp().lang_to_combo.set_active_id(Some(from_id.as_str())); - self.imp().lang_from_default_cb.set_active(self.imp().lang_to_default_cb.is_active()); - self.imp().lang_to_default_cb.set_active(self.imp().lang_from_default_cb.is_active()); + self.imp() + .lang_from_combo + .set_active_id(Some(to_id.as_str())); + self.imp() + .lang_to_combo + .set_active_id(Some(from_id.as_str())); + self.imp() + .lang_from_default_cb + .set_active(self.imp().lang_to_default_cb.is_active()); + self.imp() + .lang_to_default_cb + .set_active(self.imp().lang_from_default_cb.is_active()); } } } @@ -240,31 +317,17 @@ impl TranslatorWidget { let text = buffer.text(&buffer.start_iter(), &buffer.end_iter(), true)?; let lang_from = self.imp().lang_from_combo.active_id()?.to_string(); let lang_to = self.imp().lang_to_combo.active_id()?.to_string(); + let url = self.imp().url_field.buffer().text(); let translator = self.imp().tl_engine_combo.active_id()?.to_string(); let api_key = self.imp().api_key_field.buffer().text(); - let translatet_text = match translator.as_str() { - "google_api_v1" => { - google::GoogleApiV1::new().translate( - &text, *google::Language::from_language_code(&lang_from)?, *google::Language::from_language_code(&lang_to)? - ) - }, - "google_api_v2" => { - let mut google_api_v2 = google::GoogleApiV2::new(); - google_api_v2.set_key(api_key); - google_api_v2.translate( - &text, *google::Language::from_language_code(&lang_from)?, *google::Language::from_language_code(&lang_to)? - ) - }, - "google_scrape" => { - google::GoogleScrape::new().translate( - &text, *google::Language::from_language_code(&lang_from)?, *google::Language::from_language_code(&lang_to)? - ) - }, - _ => { return Some(()); } - }; + let mut translator = MetaTranslator::new(translator, Some(url)); + if api_key.len() > 0 { + translator.set_key(api_key); + } + let translation = translator.translate(&text, &lang_from, &lang_to); - match translatet_text { + match translation { Ok(res) => { let mut text = res.translation; if let Some(alternatives) = res.alternatives { @@ -275,11 +338,14 @@ impl TranslatorWidget { } self.imp().lang_to_field.buffer()?.set_text(&text); - }, + } Err(e) => { - self.imp().lang_to_field.buffer()?.set_text(&format!("Error: {:?}", e)); + self.imp() + .lang_to_field + .buffer()? + .set_text(&format!("Error: {:?}", e)); } - } + } Some(()) } @@ -291,7 +357,8 @@ impl TranslatorWidget { gtk::DialogFlags::MODAL, gtk::MessageType::Info, gtk::ButtonsType::Ok, - "Not implemented yet" - ).run(); - } + "Not implemented yet", + ) + .run(); + } } diff --git a/src/app/window/imp.rs b/src/app/window/imp.rs index 131265c..6e7b81d 100644 --- a/src/app/window/imp.rs +++ b/src/app/window/imp.rs @@ -1,6 +1,6 @@ use gtk::glib; -use gtk::subclass::prelude::*; use gtk::glib::once_cell::sync::OnceCell; +use gtk::subclass::prelude::*; use crate::app::channel::Message; @@ -10,7 +10,7 @@ pub struct MpvSubsWindow { pub sub_label: OnceCell, pub tl_label: OnceCell, pub label_box: OnceCell, - pub channel_sender: OnceCell> + pub channel_sender: OnceCell>, } #[glib::object_subclass] @@ -20,7 +20,6 @@ impl ObjectSubclass for MpvSubsWindow { type ParentType = gtk::ApplicationWindow; } - impl ObjectImpl for MpvSubsWindow {} impl WidgetImpl for MpvSubsWindow {} diff --git a/src/app/window/mod.rs b/src/app/window/mod.rs index d0c25b1..71450b3 100644 --- a/src/app/window/mod.rs +++ b/src/app/window/mod.rs @@ -1,9 +1,9 @@ mod imp; -use gtk::{gio, glib, Application, prelude::*}; use glib::Object; +use gtk::{gio, glib, prelude::*, Application}; -glib::wrapper! { +glib::wrapper! { pub struct MpvSubsWindow(ObjectSubclass) @extends gtk::ApplicationWindow, gtk::Window, gtk::Bin, gtk::Container, gtk::Widget, @implements gio::ActionGroup,gtk::Buildable, gio::ActionMap; @@ -18,4 +18,3 @@ impl MpvSubsWindow { self.property::("application").quit(); } } - diff --git a/src/config.rs b/src/config.rs index c28ae46..7273548 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,12 +1,13 @@ use home_config::HomeConfig; -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Default)] pub struct TlEngineConfig { pub name: String, pub api_key: String, pub default_lang_from: String, - pub default_lang_to: String + pub default_lang_to: String, + pub url: Option, } #[derive(Serialize, Deserialize, Default)] @@ -21,7 +22,7 @@ pub struct AppConfig { pub size_lock: Option<(i32, i32)>, pub auto_tl: bool, pub default_tl_engine: String, - pub translators: Vec + pub translators: Vec, } impl AppConfig { @@ -40,7 +41,7 @@ impl AppConfig { ..AppConfig::default() }; - config.json::().unwrap_or(default_config) + config.json::().unwrap_or(default_config) } pub fn config_dir() -> HomeConfig { diff --git a/src/language/mod.rs b/src/language/mod.rs index 129ab34..49c5450 100644 --- a/src/language/mod.rs +++ b/src/language/mod.rs @@ -1,44 +1,52 @@ -pub mod translators; pub mod dictionaries; +pub mod translators; #[derive(Debug)] pub enum Error { - LanguageNotAvailable(String), - Deserialization(String), - Request(String), - Google(u16), - NoTranslation + Deserialization(String), + TranslatorNotAvailable(String), + Request(String), + Google(u16), + DeeplX(u16), + NoTranslation, } #[derive(Debug)] pub struct TranslatorResponse { - pub translation: String, - pub alternatives: Option> + pub translation: String, + pub alternatives: Option>, +} + +pub fn get_tranlators_codes() -> Vec<&'static str> { + ["google_api_v1", "google_api_v2", "google_scrape", "deeplx"].to_vec() } pub mod prelude { - pub trait Translator { - fn new() -> Self where Self: Sized; - fn translate(&self, text: &str, in_lang: impl LanguageExt, out_lang: impl LanguageExt) -> Result; - fn get_name() -> String; - fn get_api_key_url() -> Option; - } + use std::collections::BTreeMap; - pub trait Dictionary { - fn new() -> Self; - fn lookup(&self, word: &str); - } + pub trait Translator { + fn new(url_override: Option) -> Self + where + Self: Sized; + fn translate( + &self, + text: &str, + in_lang_code: &str, + out_lang_code: &str, + ) -> Result; + fn get_url(&self) -> String; + fn get_name() -> String; + fn get_api_key_url() -> Option; + fn get_language_map() -> BTreeMap<&'static str, &'static str>; + } - pub trait ApiKey { - fn set_key(&mut self, key: String); - fn get_key(&self) -> Option; - } + // pub trait Dictionary { + // fn new() -> Self; + // fn lookup(&self, word: &str); + // } - pub trait LanguageExt { - fn from_language_name(name: &str) -> Option>; - fn from_language_code(code: &str) -> Option>; - fn to_language_code(&self) -> String; - fn to_language_name(&self) -> String; - fn get_iterator() -> Box> ; - } + pub trait ApiKey { + fn set_key(&mut self, key: String); + fn get_key(&self) -> Option; + } } diff --git a/src/language/translators/deeplx.rs b/src/language/translators/deeplx.rs new file mode 100644 index 0000000..23ac4e2 --- /dev/null +++ b/src/language/translators/deeplx.rs @@ -0,0 +1,162 @@ +use reqwest::{ + self, + header::{HeaderMap, HeaderValue}, +}; +use serde_json; +use std::collections::{BTreeMap, HashMap}; + +use crate::language::{prelude::*, Error, TranslatorResponse}; + +const BASE_URL: &str = "http://localhost:1188/translate"; + +pub struct DeeplX { + url: String, + key: Option, +} + +impl Translator for DeeplX { + fn new(url_override: Option) -> Self { + if let Some(x) = url_override { + DeeplX { url: x, key: None } + } else { + DeeplX { + url: BASE_URL.to_string(), + key: None, + } + } + } + + fn translate( + &self, + text: &str, + in_lang_code: &str, + out_lang_code: &str, + ) -> Result { + let client = reqwest::blocking::Client::new(); + let mut data = HashMap::<&str, &str>::new(); + if in_lang_code != "auto" { + data.insert("source_lang", &in_lang_code); + } else { + data.insert("source_lang", ""); + } + + data.insert("target_lang", &out_lang_code); + data.insert("text", text); + + let mut headers = HeaderMap::new(); + headers.insert( + reqwest::header::CONTENT_TYPE, + HeaderValue::from_static("application/json"), + ); + + if let Some(key) = &self.key { + if self.url.contains("/v2/translate") { + if let Ok(v) = HeaderValue::from_str(&format!("DeepL-Auth-Key {}", key)) { + headers.insert(reqwest::header::AUTHORIZATION, v); + } + } else { + if let Ok(v) = HeaderValue::from_str(&format!("Bearer {}", key)) { + headers.insert(reqwest::header::AUTHORIZATION, v); + } + } + }; + + let res = client.post(&self.url).json(&data).headers(headers).send(); + let json = match res { + Ok(res) => { + if res.status().as_u16() != 200 { + return Err(Error::DeeplX(res.status().as_u16())); + } + res.json::() + } + Err(error) => { + return Err(Error::Request(error.to_string())); + } + }; + + let json = match json { + Ok(json) => json, + Err(error) => { + return Err(Error::Deserialization(error.to_string())); + } + }; + + let translation = json["data"].as_str(); + + let translation = match translation { + Some(tl) => tl, + None => return Err(Error::NoTranslation), + }; + + let alternatives: Option> = json["alternatives"].as_array().map(|vec| { + vec.iter() + .filter_map(|val| val.as_str().map(|s| s.to_string())) + .collect() + }); + + Ok(TranslatorResponse { + translation: translation.to_string(), + alternatives, + }) + } + + fn get_url(&self) -> String { + self.url.to_string() + } + + fn get_name() -> String { + "DeeplX".to_string() + } + + fn get_api_key_url() -> Option { + Some("https://deeplx.owo.network/".to_string()) + } + + fn get_language_map() -> BTreeMap<&'static str, &'static str> { + let mut map = BTreeMap::new(); + + map.insert("Automatic", "auto"); + map.insert("Arabic", "AR"); + map.insert("Bulgarian", "BG"); + map.insert("Czech", "CS"); + map.insert("Danish", "DA"); + map.insert("German", "DE"); + map.insert("Greek", "EL"); + map.insert("English", "EN"); + map.insert("Spanish", "ES"); + map.insert("Estonian", "ET"); + map.insert("Finnish", "FI"); + map.insert("French", "FR"); + map.insert("Hungarian", "HU"); + map.insert("Indonesian", "ID"); + map.insert("Italian", "IT"); + map.insert("Japanese", "JA"); + map.insert("Korean", "KO"); + map.insert("Lithuanian", "LT"); + map.insert("Latvian", "LV"); + map.insert("Norwegian Bokmål", "NB"); + map.insert("Dutch", "NL"); + map.insert("Polish", "PL"); + map.insert("Portuguese", "PT"); + map.insert("Romanian", "RO"); + map.insert("Russian", "RU"); + map.insert("Slovak", "SK"); + map.insert("Slovenian", "SL"); + map.insert("Swedish", "SV"); + map.insert("Turkish", "TR"); + map.insert("Ukrainian", "UK"); + map.insert("Chinese", "ZH"); + + map + } +} + +impl ApiKey for DeeplX { + fn get_key(&self) -> Option { + self.key.clone() + } + + fn set_key(&mut self, key: String) { + self.key = Some(key); + } +} diff --git a/src/language/translators/google/googleapiv1.rs b/src/language/translators/google/googleapiv1.rs index 826721a..566ac4b 100644 --- a/src/language/translators/google/googleapiv1.rs +++ b/src/language/translators/google/googleapiv1.rs @@ -1,100 +1,116 @@ -use std::collections::HashMap; use reqwest; use serde_json; +use std::collections::{HashMap, BTreeMap}; -use crate::language::{prelude::*, TranslatorResponse, Error}; -use super::Language; - +use super::get_language_map; +use crate::language::{prelude::*, Error, TranslatorResponse}; const BASE_URL: &str = "https://translate.google.com/translate_a/single"; -pub struct GoogleApiV1 {} +pub struct GoogleApiV1 { + url: String, +} impl Translator for GoogleApiV1 { - fn new() -> Self where Self: Sized { - GoogleApiV1 { } - } - - fn translate(&self, text: &str, in_lang: impl LanguageExt, out_lang: impl LanguageExt) -> Result { - let in_lang_code = match Language::from_language_code(&in_lang.to_language_code()) { - Some(lang) => { lang.to_language_code() } , - None => { return Err( Error::LanguageNotAvailable(in_lang.to_language_name()) ) } - }; - - - let out_lang_code = match Language::from_language_code(&out_lang.to_language_code()) { - Some(lang) => { lang.to_language_code() } , - None => { return Err( Error::LanguageNotAvailable(out_lang.to_language_name()) ) } - }; - - let client = reqwest::blocking::Client::new(); - - let mut form = HashMap::<&str, &str>::new(); - form.insert("sl", &in_lang_code); - form.insert("tl", &out_lang_code); - form.insert("q", text); - - let res = client.post(BASE_URL) - .query(&[ - ("client", "gtx"), - ("dt", "t"), // with translation - ("dt", "at"), // with alternative translation - ("dj", "1") // with proper json - ]) - .header( - reqwest::header::CONTENT_TYPE, "application/x-www-form-urlencoded" - ) - .form(&form) - .send(); - - let json = match res { - Ok(res) => { - if res.status().as_u16() != 200 { - return Err(Error::Google(res.status().as_u16())); + fn new(url_override: Option) -> Self { + if let Some(x) = url_override { + GoogleApiV1 { url: x } + } else { + GoogleApiV1 { + url: BASE_URL.to_string(), + } + } + } + + fn translate( + &self, + text: &str, + in_lang_code: &str, + out_lang_code: &str, + ) -> Result { + let client = reqwest::blocking::Client::new(); + + let mut form = HashMap::<&str, &str>::new(); + form.insert("sl", &in_lang_code); + form.insert("tl", &out_lang_code); + form.insert("q", text); + + let res = client + .post(&self.url) + .query(&[ + ("client", "gtx"), + ("dt", "t"), // with translation + ("dt", "at"), // with alternative translation + ("dj", "1"), // with proper json + ]) + .header( + reqwest::header::CONTENT_TYPE, + "application/x-www-form-urlencoded", + ) + .form(&form) + .send(); + + let json = match res { + Ok(res) => { + if res.status().as_u16() != 200 { + return Err(Error::Google(res.status().as_u16())); + } + res.json::() + } + Err(error) => { + return Err(Error::Request(error.to_string())); + } + }; + + let json = match json { + Ok(json) => json, + Err(error) => { + return Err(Error::Deserialization(error.to_string())); + } + }; + + let translation = json["sentences"][0]["trans"].as_str(); + + let translation = match translation { + Some(trans) => trans, + None => { + return Err(Error::NoTranslation); } - res.json::() - }, - Err(error) => { - return Err(Error::Request(error.to_string())); - } - }; - - let json = match json { - Ok(json) => { json }, - Err(error) => { - return Err(Error::Deserialization(error.to_string())); - } - }; - - let translation = json["sentences"][0]["trans"].as_str(); - - let translation = match translation { - Some(trans) => {trans}, - None => {return Err(Error::NoTranslation);} - }; - - let alternatives = json["alternative_translations"][0]["alternative"] - .as_array(); - - let alternatives = alternatives.map(|alternatives| alternatives.iter() - .map(|val| { - val["word_postproc"].as_str().unwrap_or_default().to_string() - }) - .collect()); - - - Ok(TranslatorResponse { - translation: translation.to_string(), - alternatives - }) - } - - fn get_name() -> String { - "Google Translate Api V1".to_string() - } - - - fn get_api_key_url() -> Option { - None - } + }; + + let alternatives = json["alternative_translations"][0]["alternative"].as_array(); + + let alternatives = alternatives.map(|alternatives| { + alternatives + .iter() + .map(|val| { + val["word_postproc"] + .as_str() + .unwrap_or_default() + .to_string() + }) + .collect() + }); + + Ok(TranslatorResponse { + translation: translation.to_string(), + alternatives, + }) + } + + fn get_url(&self) -> String { + self.url.to_string() + } + + fn get_name() -> String { + "Google Translate Api V1".to_string() + } + + fn get_api_key_url() -> Option { + None + } + + fn get_language_map() -> BTreeMap<&'static str, &'static str> { + get_language_map() + } } diff --git a/src/language/translators/google/googleapiv2.rs b/src/language/translators/google/googleapiv2.rs index 2fc0725..6d8ab47 100644 --- a/src/language/translators/google/googleapiv2.rs +++ b/src/language/translators/google/googleapiv2.rs @@ -1,90 +1,99 @@ +use std::collections::BTreeMap; + use reqwest; use serde_json; -use crate::language::{prelude::*, TranslatorResponse, Error}; -use super::Language; - +use super::get_language_map; +use crate::language::{prelude::*, Error, TranslatorResponse}; const BASE_URL: &str = "https://translation.googleapis.com/language/translate/v2"; pub struct GoogleApiV2 { - key: Option + url: String, + key: Option, } impl Translator for GoogleApiV2 { - fn new() -> Self { - GoogleApiV2 { key: None } + fn new(url_override: Option) -> Self { + if let Some(x) = url_override { + GoogleApiV2 { url: x, key: None } + } else { + GoogleApiV2 { + url: BASE_URL.to_string(), + key: None, + } + } } - fn translate(&self, text: &str, in_lang: impl LanguageExt, out_lang: impl LanguageExt) -> Result { - let in_lang_code = match Language::from_language_code(&in_lang.to_language_code()) { - Some(lang) => { lang.to_language_code() } , - None => { return Err( Error::LanguageNotAvailable(in_lang.to_language_name()) ) } - }; - - - let out_lang_code = match Language::from_language_code(&out_lang.to_language_code()) { - Some(lang) => { lang.to_language_code() } , - None => { return Err( Error::LanguageNotAvailable(out_lang.to_language_name()) ) } - }; - - + fn translate( + &self, + text: &str, + in_lang_code: &str, + out_lang_code: &str, + ) -> Result { let client = reqwest::blocking::Client::new(); let res = client - .post(BASE_URL) + .post(&self.url) .query(&[ ("format", "text"), ("target", &out_lang_code), ("source", &in_lang_code), ("key", self.key.as_ref().unwrap()), - ("q", text) + ("q", text), ]) .send(); - + let json = match res { Ok(res) => { if res.status().as_u16() != 200 { return Err(Error::Google(res.status().as_u16())); } res.json::() - }, - Err(error) => { + } + Err(error) => { return Err(Error::Request(error.to_string())); - } + } }; let json = match json { - Ok(json) => { json }, - Err(error) => { - return Err(Error::Deserialization(error.to_string())); + Ok(json) => json, + Err(error) => { + return Err(Error::Deserialization(error.to_string())); } }; let translation = json["data"]["translations"][0]["translatedText"].as_str(); match translation { - Some(trans) => { - Ok(TranslatorResponse { translation: trans.to_string(), alternatives: None }) - }, - None => {Err(Error::NoTranslation)} + Some(trans) => Ok(TranslatorResponse { + translation: trans.to_string(), + alternatives: None, + }), + None => Err(Error::NoTranslation), } } - fn get_name() -> String { + fn get_url(&self) -> String { + self.url.to_string() + } + + fn get_name() -> String { "Google Translate Api V2".to_string() } fn get_api_key_url() -> Option { Some( "https://console.cloud.google.com/marketplace/product/google/translate.googleapis.com" - .to_string() + .to_string(), ) } + fn get_language_map() -> BTreeMap<&'static str, &'static str> { + get_language_map() + } } - impl ApiKey for GoogleApiV2 { fn get_key(&self) -> Option { self.key.clone() diff --git a/src/language/translators/google/googlescrape.rs b/src/language/translators/google/googlescrape.rs index d7b986a..ec782a5 100644 --- a/src/language/translators/google/googlescrape.rs +++ b/src/language/translators/google/googlescrape.rs @@ -1,93 +1,100 @@ +use std::collections::BTreeMap; - -use reqwest; use regex::Regex; +use reqwest; -use crate::language::{prelude::*, TranslatorResponse, Error}; -use super::Language; - +use super::get_language_map; +use crate::language::{prelude::*, Error, TranslatorResponse}; const BASE_URL: &str = "https://translate.google.com/m"; -pub struct GoogleScrape; +pub struct GoogleScrape { + url: String, +} impl Translator for GoogleScrape { - fn new() -> Self { - GoogleScrape { } + fn new(url_override: Option) -> Self { + if let Some(x) = url_override { + GoogleScrape { url: x } + } else { + GoogleScrape { + url: BASE_URL.to_string(), + } + } } - fn translate(&self, text: &str, in_lang: impl LanguageExt, out_lang: impl LanguageExt) -> Result { - let in_lang_code = match Language::from_language_code(&in_lang.to_language_code()) { - Some(lang) => { lang.to_language_code() } , - None => { return Err( Error::LanguageNotAvailable(in_lang.to_language_name()) ) } - }; - - - let out_lang_code = match Language::from_language_code(&out_lang.to_language_code()) { - Some(lang) => { lang.to_language_code() } , - None => { return Err( Error::LanguageNotAvailable(out_lang.to_language_name()) ) } - }; - - let client = reqwest::blocking::Client::new(); + fn translate( + &self, + text: &str, + in_lang_code: &str, + out_lang_code: &str, + ) -> Result { + let client = reqwest::blocking::Client::new(); - let res = client.get(BASE_URL) + let res = client.get(&self.url) .query(&[ ("sl", in_lang_code), ("tl", out_lang_code), - ("q", text.to_string()) + ("q", text) ]) .header( reqwest::header::USER_AGENT, "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36" ) .send(); - - let text = match res { - Ok(res) => { - if res.status().as_u16() != 200 { - return Err(Error::Google(res.status().as_u16())); + + let text = match res { + Ok(res) => { + if res.status().as_u16() != 200 { + return Err(Error::Google(res.status().as_u16())); + } + res.text() + } + Err(error) => { + return Err(Error::Request(error.to_string())); } - res.text() - }, - Err(error) => { - return Err(Error::Request(error.to_string())); - } - }; - - let text = match text { - Ok(text) => { text }, - Err(error) => { - return Err(Error::Deserialization(error.to_string())); - } - }; - - - let re = Regex::new("result-container\">([^<]+)").unwrap(); - let capture = re.captures(&text); - - let translation = match capture { - Some(capture) => { - match capture.get(1) { - Some(trans) => { - trans.as_str() - }, - None => {return Err(Error::NoTranslation); } + }; + + let text = match text { + Ok(text) => text, + Err(error) => { + return Err(Error::Deserialization(error.to_string())); } - }, - None => { return Err(Error::NoTranslation); } - }; - - - Ok(TranslatorResponse { - translation: translation.to_string(), - alternatives: None - }) + }; + + let re = Regex::new("result-container\">([^<]+)").unwrap(); + let capture = re.captures(&text); + + let translation = match capture { + Some(capture) => match capture.get(1) { + Some(trans) => trans.as_str(), + None => { + return Err(Error::NoTranslation); + } + }, + None => { + return Err(Error::NoTranslation); + } + }; + + Ok(TranslatorResponse { + translation: translation.to_string(), + alternatives: None, + }) + } + + fn get_url(&self) -> String { + self.url.to_string() } fn get_name() -> String { - "Google Translate Scraper".to_string() + "Google Translate Scraper".to_string() } fn get_api_key_url() -> Option { - None + None + } + + fn get_language_map() -> BTreeMap<&'static str, &'static str> { + get_language_map() } } diff --git a/src/language/translators/google/mod.rs b/src/language/translators/google/mod.rs index 9368928..609bfaf 100644 --- a/src/language/translators/google/mod.rs +++ b/src/language/translators/google/mod.rs @@ -2,313 +2,148 @@ pub mod googleapiv1; pub mod googleapiv2; pub mod googlescrape; -use strum_macros; -use strum::{EnumProperty, IntoEnumIterator}; -use std::str::FromStr; - -use crate::language::{prelude::*}; +use std::collections::BTreeMap; pub use googleapiv1::GoogleApiV1; pub use googleapiv2::GoogleApiV2; pub use googlescrape::GoogleScrape; +fn get_language_map() -> BTreeMap<&'static str, &'static str> { + let mut map = BTreeMap::new(); + map.insert("Automatic", "auto"); + map.insert("Afrikaans", "af"); + map.insert("Albanian", "sq"); + map.insert("Amharic", "am"); + map.insert("Arabic", "ar"); + map.insert("Armenian", "hy"); + map.insert("Assamese", "as"); + map.insert("Aymara", "ay"); + map.insert("Azerbaijani", "az"); + map.insert("Bambara", "bm"); + map.insert("Basque", "eu"); + map.insert("Belarusian", "be"); + map.insert("Bengali", "bn"); + map.insert("Bhojpuri", "bho"); + map.insert("Bosnian", "bs"); + map.insert("Bulgarian", "bg"); + map.insert("Catalan", "ca"); + map.insert("Cebuano", "ceb"); + map.insert("ChineseSimplified", "zh"); + map.insert("ChineseTraditional", "zh-TW"); + map.insert("Corsican", "co"); + map.insert("Croatian", "hr"); + map.insert("Czech", "cs"); + map.insert("Danish", "da"); + map.insert("Dhivehi", "dv"); + map.insert("Dogri", "doi"); + map.insert("Dutch", "nl"); + map.insert("English", "en"); + map.insert("Esperanto", "eo"); + map.insert("Estonian", "et"); + map.insert("Ewe", "ee"); + map.insert("Filipino", "fil"); + map.insert("Finnish", "fi"); + map.insert("French", "fr"); + map.insert("Frisian", "fy"); + map.insert("Galician", "gl"); + map.insert("Georgian", "ka"); + map.insert("German", "de"); + map.insert("Greek", "el"); + map.insert("Guarani", "gn"); + map.insert("Gujarati", "gu"); + map.insert("Haitian Creole", "ht"); + map.insert("Hausa", "ha"); + map.insert("Hawaiian", "haw"); + map.insert("Hebrew", "he or iw"); + map.insert("Hindi", "hi"); + map.insert("Hmong", "hmn"); + map.insert("Hungarian", "hu"); + map.insert("Icelandic", "is"); + map.insert("Igbo", "ig"); + map.insert("Ilocano", "ilo"); + map.insert("Indonesian", "id"); + map.insert("Irish", "ga"); + map.insert("Italian", "it"); + map.insert("Japanese", "ja"); + map.insert("Javanese", "jv"); + map.insert("Kannada", "kn"); + map.insert("Kazakh", "kk"); + map.insert("Khmer", "km"); + map.insert("Kinyarwanda", "rw"); + map.insert("Konkani", "gom"); + map.insert("Korean", "ko"); + map.insert("Krio", "kri"); + map.insert("Kurdish", "ckb"); + map.insert("Kyrgyz", "ky"); + map.insert("Lao", "lo"); + map.insert("Latin", "la"); + map.insert("Latvian", "lv"); + map.insert("Lingala", "ln"); + map.insert("Lithuanian", "lt"); + map.insert("Luganda", "lg"); + map.insert("Luxembourgish", "lb"); + map.insert("Macedonian", "mk"); + map.insert("Maithili", "mai"); + map.insert("Malagasy", "mg"); + map.insert("Malay", "ms"); + map.insert("Malayalam", "ml"); + map.insert("Maltese", "mt"); + map.insert("Maori", "mi"); + map.insert("Marathi", "mr"); + map.insert("Meiteilon", "mni-Mtei"); + map.insert("Mizo", "lus"); + map.insert("Mongolian", "mn"); + map.insert("Myanmar", "my"); + map.insert("Nepali", "ne"); + map.insert("Norwegian", "no"); + map.insert("Nyanja", "ny"); + map.insert("Odia", "or"); + map.insert("Oromo", "om"); + map.insert("Pashto", "ps"); + map.insert("Persian", "fa"); + map.insert("Polish", "pl"); + map.insert("Portuguese", "pt"); + map.insert("Punjabi", "pa"); + map.insert("Quechua", "qu"); + map.insert("Romanian", "ro"); + map.insert("Russian", "ru"); + map.insert("Samoan", "sm"); + map.insert("Sanskrit", "sa"); + map.insert("Scots Gaelic", "gd"); + map.insert("Sepedi", "nso"); + map.insert("Serbian", "sr"); + map.insert("Sesotho", "st"); + map.insert("Shona", "sn"); + map.insert("Sindhi", "sd"); + map.insert("Sinhalese", "si"); + map.insert("Slovak", "sk"); + map.insert("Slovenian", "sl"); + map.insert("Somali", "so"); + map.insert("Spanish", "es"); + map.insert("Sundanese", "su"); + map.insert("Swahili", "sw"); + map.insert("Swedish", "sv"); + map.insert("Tagalog", "tl"); + map.insert("Tajik", "tg"); + map.insert("Tamil", "ta"); + map.insert("Tatar", "tt"); + map.insert("Telugu", "te"); + map.insert("Thai", "th"); + map.insert("Tigrinya", "ti"); + map.insert("Tsonga", "ts"); + map.insert("Turkish", "tr"); + map.insert("Turkmen", "tk"); + map.insert("Akan", "ak"); + map.insert("Ukrainian", "uk"); + map.insert("Urdu", "ur"); + map.insert("Uyghur", "ug"); + map.insert("Uzbek", "uz"); + map.insert("Vietnamese", "vi"); + map.insert("Welsh", "cy"); + map.insert("Xhosa", "xh"); + map.insert("Yiddish", "yi"); + map.insert("Yoruba", "yo"); + map.insert("Zulu", "zu"); -#[derive(strum_macros::EnumProperty, strum_macros::Display, strum_macros::EnumString, strum_macros::EnumIter)] -pub enum Language { - #[strum(props(code = "auto"), serialize = "auto", serialize = "Automatic")] - Automatic, - #[strum(props(code = "af"), serialize = "af", serialize = "Afrikaans")] - Afrikaans, - #[strum(props(code = "sq"), serialize = "sq", serialize = "Albanian")] - Albanian, - #[strum(props(code = "am"), serialize = "am", serialize = "Amharic")] - Amharic, - #[strum(props(code = "ar"), serialize = "ar", serialize = "Arabic")] - Arabic, - #[strum(props(code = "hy"), serialize = "hy", serialize = "Armenian")] - Armenian, - #[strum(props(code = "as"), serialize = "as", serialize = "Assamese")] - Assamese, - #[strum(props(code = "ay"), serialize = "ay", serialize = "Aymara")] - Aymara, - #[strum(props(code = "az"), serialize = "az", serialize = "Azerbaijani")] - Azerbaijani, - #[strum(props(code = "bm"), serialize = "bm", serialize = "Bambara")] - Bambara, - #[strum(props(code = "eu"), serialize = "eu", serialize = "Basque")] - Basque, - #[strum(props(code = "be"), serialize = "be", serialize = "Belarusian")] - Belarusian, - #[strum(props(code = "bn"), serialize = "bn", serialize = "Bengali")] - Bengali, - #[strum(props(code = "bho"), serialize = "bho", serialize = "Bhojpuri")] - Bhojpuri, - #[strum(props(code = "bs"), serialize = "bs", serialize = "Bosnian")] - Bosnian, - #[strum(props(code = "bg"), serialize = "bg", serialize = "Bulgarian")] - Bulgarian, - #[strum(props(code = "ca"), serialize = "ca", serialize = "Catalan")] - Catalan, - #[strum(props(code = "ceb"), serialize = "ceb", serialize = "Cebuano")] - Cebuano, - #[strum(props(code = "zh"), serialize = "zh", serialize = "ChineseSimplified")] - ChineseSimplified, - #[strum(props(code = "zh-TW"), serialize = "zh-TW", serialize = "ChineseTraditional")] - ChineseTraditional, - #[strum(props(code = "co"), serialize = "co", serialize = "Corsican")] - Corsican, - #[strum(props(code = "hr"), serialize = "hr", serialize = "Croatian")] - Croatian, - #[strum(props(code = "cs"), serialize = "cs", serialize = "Czech")] - Czech, - #[strum(props(code = "da"), serialize = "da", serialize = "Danish")] - Danish, - #[strum(props(code = "dv"), serialize = "dv", serialize = "Dhivehi")] - Dhivehi, - #[strum(props(code = "doi"), serialize = "doi", serialize = "Dogri")] - Dogri, - #[strum(props(code = "nl"), serialize = "nl", serialize = "Dutch")] - Dutch, - #[strum(props(code = "en"), serialize = "en", serialize = "English")] - English, - #[strum(props(code = "eo"), serialize = "eo", serialize = "Esperanto")] - Esperanto, - #[strum(props(code = "et"), serialize = "et", serialize = "Estonian")] - Estonian, - #[strum(props(code = "ee"), serialize = "ee", serialize = "Ewe")] - Ewe, - #[strum(props(code = "fil"), serialize = "fil", serialize = "Filipino")] - Filipino, - #[strum(props(code = "fi"), serialize = "fi", serialize = "Finnish")] - Finnish, - #[strum(props(code = "fr"), serialize = "fr", serialize = "French")] - French, - #[strum(props(code = "fy"), serialize = "fy", serialize = "Frisian")] - Frisian, - #[strum(props(code = "gl"), serialize = "gl", serialize = "Galician")] - Galician, - #[strum(props(code = "ka"), serialize = "ka", serialize = "Georgian")] - Georgian, - #[strum(props(code = "de"), serialize = "de", serialize = "German")] - German, - #[strum(props(code = "el"), serialize = "el", serialize = "Greek")] - Greek, - #[strum(props(code = "gn"), serialize = "gn", serialize = "Guarani")] - Guarani, - #[strum(props(code = "gu"), serialize = "gu", serialize = "Gujarati")] - Gujarati, - #[strum(props(code = "ht"), serialize = "ht", serialize = "Haitian Creole")] - HaitianCreole, - #[strum(props(code = "ha"), serialize = "ha", serialize = "Hausa")] - Hausa, - #[strum(props(code = "haw"), serialize = "haw", serialize = "Hawaiian")] - Hawaiian, - #[strum(props(code = "he or iw"), serialize = "he or iw", serialize = "Hebrew")] - Hebrew, - #[strum(props(code = "hi"), serialize = "hi", serialize = "Hindi")] - Hindi, - #[strum(props(code = "hmn"), serialize = "hmn", serialize = "Hmong")] - Hmong, - #[strum(props(code = "hu"), serialize = "hu", serialize = "Hungarian")] - Hungarian, - #[strum(props(code = "is"), serialize = "is", serialize = "Icelandic")] - Icelandic, - #[strum(props(code = "ig"), serialize = "ig", serialize = "Igbo")] - Igbo, - #[strum(props(code = "ilo"), serialize = "ilo", serialize = "Ilocano")] - Ilocano, - #[strum(props(code = "id"), serialize = "id", serialize = "Indonesian")] - Indonesian, - #[strum(props(code = "ga"), serialize = "ga", serialize = "Irish")] - Irish, - #[strum(props(code = "it"), serialize = "it", serialize = "Italian")] - Italian, - #[strum(props(code = "ja"), serialize = "ja", serialize = "Japanese")] - Japanese, - #[strum(props(code = "jv"), serialize = "jv", serialize = "Javanese")] - Javanese, - #[strum(props(code = "kn"), serialize = "kn", serialize = "Kannada")] - Kannada, - #[strum(props(code = "kk"), serialize = "kk", serialize = "Kazakh")] - Kazakh, - #[strum(props(code = "km"), serialize = "km", serialize = "Khmer")] - Khmer, - #[strum(props(code = "rw"), serialize = "rw", serialize = "Kinyarwanda")] - Kinyarwanda, - #[strum(props(code = "gom"), serialize = "gom", serialize = "Konkani")] - Konkani, - #[strum(props(code = "ko"), serialize = "ko", serialize = "Korean")] - Korean, - #[strum(props(code = "kri"), serialize = "kri", serialize = "Krio")] - Krio, - #[strum(props(code = "ckb"), serialize = "ckb", serialize = "Kurdish")] - Kurdish, - #[strum(props(code = "ky"), serialize = "ky", serialize = "Kyrgyz")] - Kyrgyz, - #[strum(props(code = "lo"), serialize = "lo", serialize = "Lao")] - Lao, - #[strum(props(code = "la"), serialize = "la", serialize = "Latin")] - Latin, - #[strum(props(code = "lv"), serialize = "lv", serialize = "Latvian")] - Latvian, - #[strum(props(code = "ln"), serialize = "ln", serialize = "Lingala")] - Lingala, - #[strum(props(code = "lt"), serialize = "lt", serialize = "Lithuanian")] - Lithuanian, - #[strum(props(code = "lg"), serialize = "lg", serialize = "Luganda")] - Luganda, - #[strum(props(code = "lb"), serialize = "lb", serialize = "Luxembourgish")] - Luxembourgish, - #[strum(props(code = "mk"), serialize = "mk", serialize = "Macedonian")] - Macedonian, - #[strum(props(code = "mai"), serialize = "mai", serialize = "Maithili")] - Maithili, - #[strum(props(code = "mg"), serialize = "mg", serialize = "Malagasy")] - Malagasy, - #[strum(props(code = "ms"), serialize = "ms", serialize = "Malay")] - Malay, - #[strum(props(code = "ml"), serialize = "ml", serialize = "Malayalam")] - Malayalam, - #[strum(props(code = "mt"), serialize = "mt", serialize = "Maltese")] - Maltese, - #[strum(props(code = "mi"), serialize = "mi", serialize = "Maori")] - Maori, - #[strum(props(code = "mr"), serialize = "mr", serialize = "Marathi")] - Marathi, - #[strum(props(code = "mni-Mtei"), serialize = "mni-Mtei", serialize = "Meiteilon")] - Meiteilon, - #[strum(props(code = "lus"), serialize = "lus", serialize = "Mizo")] - Mizo, - #[strum(props(code = "mn"), serialize = "mn", serialize = "Mongolian")] - Mongolian, - #[strum(props(code = "my"), serialize = "my", serialize = "Myanmar")] - Myanmar, - #[strum(props(code = "ne"), serialize = "ne", serialize = "Nepali")] - Nepali, - #[strum(props(code = "no"), serialize = "no", serialize = "Norwegian")] - Norwegian, - #[strum(props(code = "ny"), serialize = "ny", serialize = "Nyanja")] - Nyanja, - #[strum(props(code = "or"), serialize = "or", serialize = "Odia")] - Odia, - #[strum(props(code = "om"), serialize = "om", serialize = "Oromo")] - Oromo, - #[strum(props(code = "ps"), serialize = "ps", serialize = "Pashto")] - Pashto, - #[strum(props(code = "fa"), serialize = "fa", serialize = "Persian")] - Persian, - #[strum(props(code = "pl"), serialize = "pl", serialize = "Polish")] - Polish, - #[strum(props(code = "pt"), serialize = "pt", serialize = "Portuguese")] - Portuguese, - #[strum(props(code = "pa"), serialize = "pa", serialize = "Punjabi")] - Punjabi, - #[strum(props(code = "qu"), serialize = "qu", serialize = "Quechua")] - Quechua, - #[strum(props(code = "ro"), serialize = "ro", serialize = "Romanian")] - Romanian, - #[strum(props(code = "ru"), serialize = "ru", serialize = "Russian")] - Russian, - #[strum(props(code = "sm"), serialize = "sm", serialize = "Samoan")] - Samoan, - #[strum(props(code = "sa"), serialize = "sa", serialize = "Sanskrit")] - Sanskrit, - #[strum(props(code = "gd"), serialize = "gd", serialize = "Scots Gaelic")] - ScotsGaelic, - #[strum(props(code = "nso"), serialize = "nso", serialize = "Sepedi")] - Sepedi, - #[strum(props(code = "sr"), serialize = "sr", serialize = "Serbian")] - Serbian, - #[strum(props(code = "st"), serialize = "st", serialize = "Sesotho")] - Sesotho, - #[strum(props(code = "sn"), serialize = "sn", serialize = "Shona")] - Shona, - #[strum(props(code = "sd"), serialize = "sd", serialize = "Sindhi")] - Sindhi, - #[strum(props(code = "si"), serialize = "si", serialize = "Sinhalese")] - Sinhalese, - #[strum(props(code = "sk"), serialize = "sk", serialize = "Slovak")] - Slovak, - #[strum(props(code = "sl"), serialize = "sl", serialize = "Slovenian")] - Slovenian, - #[strum(props(code = "so"), serialize = "so", serialize = "Somali")] - Somali, - #[strum(props(code = "es"), serialize = "es", serialize = "Spanish")] - Spanish, - #[strum(props(code = "su"), serialize = "su", serialize = "Sundanese")] - Sundanese, - #[strum(props(code = "sw"), serialize = "sw", serialize = "Swahili")] - Swahili, - #[strum(props(code = "sv"), serialize = "sv", serialize = "Swedish")] - Swedish, - #[strum(props(code = "tl"), serialize = "tl", serialize = "Tagalog")] - Tagalog, - #[strum(props(code = "tg"), serialize = "tg", serialize = "Tajik")] - Tajik, - #[strum(props(code = "ta"), serialize = "ta", serialize = "Tamil")] - Tamil, - #[strum(props(code = "tt"), serialize = "tt", serialize = "Tatar")] - Tatar, - #[strum(props(code = "te"), serialize = "te", serialize = "Telugu")] - Telugu, - #[strum(props(code = "th"), serialize = "th", serialize = "Thai")] - Thai, - #[strum(props(code = "ti"), serialize = "ti", serialize = "Tigrinya")] - Tigrinya, - #[strum(props(code = "ts"), serialize = "ts", serialize = "Tsonga")] - Tsonga, - #[strum(props(code = "tr"), serialize = "tr", serialize = "Turkish")] - Turkish, - #[strum(props(code = "tk"), serialize = "tk", serialize = "Turkmen")] - Turkmen, - #[strum(props(code = "ak"), serialize = "ak", serialize = "Akan")] - Akan, - #[strum(props(code = "uk"), serialize = "uk", serialize = "Ukrainian")] - Ukrainian, - #[strum(props(code = "ur"), serialize = "ur", serialize = "Urdu")] - Urdu, - #[strum(props(code = "ug"), serialize = "ug", serialize = "Uyghur")] - Uyghur, - #[strum(props(code = "uz"), serialize = "uz", serialize = "Uzbek")] - Uzbek, - #[strum(props(code = "vi"), serialize = "vi", serialize = "Vietnamese")] - Vietnamese, - #[strum(props(code = "cy"), serialize = "cy", serialize = "Welsh")] - Welsh, - #[strum(props(code = "xh"), serialize = "xh", serialize = "Xhosa")] - Xhosa, - #[strum(props(code = "yi"), serialize = "yi", serialize = "Yiddish")] - Yiddish, - #[strum(props(code = "yo"), serialize = "yo", serialize = "Yoruba")] - Yoruba, - #[strum(props(code = "zu"), serialize = "zu", serialize = "Zulu")] - Zulu -} - - -impl LanguageExt for Language { - fn from_language_code(code: &str) -> Option> { - match Language::from_str(code) { - Ok(lang) => { - Some(Box::new(lang)) - }, - Err(..) => { None } - } - } - - fn from_language_name(name: &str) -> Option> { - Language::from_language_code(name) - } - - fn to_language_code(&self) -> String { - self.get_str("code").unwrap().to_string() - } - - fn to_language_name(&self) -> String { - self.to_string() - } - - fn get_iterator() -> Box> { - Box::new(Language::iter()) - } + map } diff --git a/src/language/translators/meta.rs b/src/language/translators/meta.rs new file mode 100644 index 0000000..f9d5d05 --- /dev/null +++ b/src/language/translators/meta.rs @@ -0,0 +1,108 @@ +use std::collections::BTreeMap; + +use crate::language::{ + prelude::*, + translators::{deeplx, google}, + Error, TranslatorResponse, +}; + +pub struct MetaTranslator { + url_override: Option, + key: Option, + translator: String, +} + +impl MetaTranslator { + pub fn new(translator: String, url_override: Option) -> Self { + MetaTranslator { + url_override, + key: None, + translator, + } + } + + pub fn translate( + &self, + text: &str, + in_lang_code: &str, + out_lang_code: &str, + ) -> Result { + match self.translator.as_str() { + "google_api_v1" => google::GoogleApiV1::new(self.url_override.clone()).translate( + text, + in_lang_code, + out_lang_code, + ), + "google_scrape" => google::GoogleScrape::new(self.url_override.clone()).translate( + text, + in_lang_code, + out_lang_code, + ), + "google_api_v2" => { + let mut google_api_v2 = google::GoogleApiV2::new(self.url_override.clone()); + if let Some(key) = self.key.clone() { + google_api_v2.set_key(key); + } + google_api_v2.translate(text, in_lang_code, out_lang_code) + } + "deeplx" => { + let mut deeplx = deeplx::DeeplX::new(self.url_override.clone()); + if let Some(key) = self.key.clone() { + deeplx.set_key(key); + } + deeplx.translate(text, in_lang_code, out_lang_code) + } + _ => Err(Error::TranslatorNotAvailable(self.translator.clone())), + } + } + + pub fn get_url(&self) -> String { + match self.translator.as_str() { + "google_api_v1" => google::GoogleApiV1::new(self.url_override.clone()).get_url(), + "google_api_v2" => google::GoogleApiV2::new(self.url_override.clone()).get_url(), + "google_scrape" => google::GoogleScrape::new(self.url_override.clone()).get_url(), + "deeplx" => deeplx::DeeplX::new(self.url_override.clone()).get_url(), + _ => "".to_string(), + } + } + + pub fn get_name(&self) -> String { + match self.translator.as_str() { + "google_api_v1" => google::GoogleApiV1::get_name(), + "google_api_v2" => google::GoogleApiV2::get_name(), + "google_scrape" => google::GoogleScrape::get_name(), + "deeplx" => deeplx::DeeplX::get_name(), + _ => "".to_string(), + } + } + + pub fn get_api_key_url(&self) -> Option { + match self.translator.as_str() { + "google_api_v1" => google::GoogleApiV1::get_api_key_url(), + "google_api_v2" => google::GoogleApiV2::get_api_key_url(), + "google_scrape" => google::GoogleScrape::get_api_key_url(), + "deeplx" => deeplx::DeeplX::get_api_key_url(), + _ => None, + } + } + + pub fn get_language_map(&self) -> BTreeMap<&'static str, &'static str> { + match self.translator.as_str() { + "google_api_v1" => google::GoogleApiV1::get_language_map(), + "google_api_v2" => google::GoogleApiV2::get_language_map(), + "google_scrape" => google::GoogleScrape::get_language_map(), + "deeplx" => deeplx::DeeplX::get_language_map(), + _ => BTreeMap::new(), + } + } +} + +impl ApiKey for MetaTranslator { + fn get_key(&self) -> Option { + self.key.clone() + } + + fn set_key(&mut self, key: String) { + self.key = Some(key); + } +} diff --git a/src/language/translators/mod.rs b/src/language/translators/mod.rs index 05c5c74..ea79d4e 100644 --- a/src/language/translators/mod.rs +++ b/src/language/translators/mod.rs @@ -1 +1,3 @@ -pub mod google; \ No newline at end of file +pub mod deeplx; +pub mod google; +pub mod meta; diff --git a/src/main.rs b/src/main.rs index 9d5387b..2a1d068 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,15 @@ // Dont Start with Terminal on Windows #![windows_subsystem = "windows"] -mod config; mod app; -mod mpv; +mod config; mod language; +mod mpv; use std::panic; -use gtk::{prelude::*, subclass::prelude::ObjectSubclassIsExt, gio}; use gtk::Application; +use gtk::{gio, prelude::*, subclass::prelude::ObjectSubclassIsExt}; use app::build_window; use app::channel::Message; @@ -32,13 +32,20 @@ fn main() { window.set_visual( GtkWindowExt::screen(&window) - .unwrap() - .rgba_visual().as_ref() + .unwrap() + .rgba_visual() + .as_ref(), ); window.show_all(); load_settings(&window); - window.imp().channel_sender.get().unwrap().send(Message::SpawnThread).ok(); + window + .imp() + .channel_sender + .get() + .unwrap() + .send(Message::SpawnThread) + .ok(); }); application.run(); diff --git a/src/mpv.rs b/src/mpv.rs index 8242f43..88f4b62 100644 --- a/src/mpv.rs +++ b/src/mpv.rs @@ -1,17 +1,26 @@ use gtk::glib; +use std::sync::{Arc, Mutex}; use std::thread; use std::time; -use std::sync::{Arc, Mutex}; use crate::app::channel::Message; -use crate::language::{prelude::*, translators::google}; use crate::config::*; +use crate::language::prelude::ApiKey; +use crate::language::translators::meta::MetaTranslator; -use mpvipc::{Mpv, Event, Property, MpvDataType}; +use mpvipc::{Event, Mpv, MpvDataType, Property}; pub fn mpv_subs_update(sender: glib::Sender) { - sender.send(Message::UpdateLabel("Waiting for an MPV instance.".to_owned())).ok(); - sender.send(Message::UpdateTlLabel("Waiting for an MPV instance.".to_owned())).ok(); + sender + .send(Message::UpdateLabel( + "Waiting for an MPV instance.".to_owned(), + )) + .ok(); + sender + .send(Message::UpdateTlLabel( + "Waiting for an MPV instance.".to_owned(), + )) + .ok(); #[cfg(target_os = "linux")] let server_path = "/tmp/mpvsock"; @@ -22,7 +31,11 @@ pub fn mpv_subs_update(sender: glib::Sender) { let mut mpv_conn: Mpv; loop { if let Ok(mpv) = Mpv::connect(server_path) { - sender.send(Message::UpdateLabel("Connected to an MPV instance! Subs will be displayed here.".to_owned())).ok(); + sender + .send(Message::UpdateLabel( + "Connected to an MPV instance! Subs will be displayed here.".to_owned(), + )) + .ok(); mpv_conn = mpv; break; @@ -31,112 +44,108 @@ pub fn mpv_subs_update(sender: glib::Sender) { thread::sleep(time::Duration::from_secs(1)); } - mpv_conn.observe_property(1, "sub-text").expect("Failed to observe property! "); - + mpv_conn + .observe_property(1, "sub-text") + .expect("Failed to observe property! "); + let mut cancel_token = Arc::new(Mutex::new(false)); while let Ok(sub_text) = get_sub_text(&mut mpv_conn) { if let Some(text) = sub_text { sender.send(Message::UpdateLabel(text.clone())).ok(); - + let cfg = AppConfig::new(); if !cfg.auto_tl { continue; } - + if text.is_empty() { sender.send(Message::UpdateTlLabel("".to_string())).ok(); continue; } - let tl_cfg = cfg.translators + let tl_cfg = cfg + .translators .into_iter() .find(|t| t.name == cfg.default_tl_engine); if let Some(tl_cfg) = tl_cfg { if tl_cfg.default_lang_to.is_empty() || tl_cfg.default_lang_from.is_empty() { - sender.send(Message::UpdateTlLabel("Set Translator Defaults first to use this.".to_string())).ok(); + sender + .send(Message::UpdateTlLabel( + "Set Translator Defaults first to use this.".to_string(), + )) + .ok(); continue; } *cancel_token.lock().unwrap() = false; cancel_token = Arc::new(Mutex::new(false)); - + let cancel_token_clone = Arc::clone(&cancel_token); let sender_clone = sender.clone(); thread::spawn(move || { tl_and_set(text, tl_cfg, sender_clone, cancel_token_clone); }); } else { - sender.send(Message::UpdateTlLabel("Set Translator Defaults first to use this.".to_string())).ok(); + sender + .send(Message::UpdateTlLabel( + "Set Translator Defaults first to use this.".to_string(), + )) + .ok(); continue; } } } } - - fn tl_and_set( text: String, tl_engine_config: TlEngineConfig, sender: glib::Sender, - cancel_token: Arc> + cancel_token: Arc>, ) -> Option<()> { let lang_from = tl_engine_config.default_lang_from; let lang_to = tl_engine_config.default_lang_to; - let translatet_text = match tl_engine_config.name.as_str() { - "google_api_v1" => { - google::GoogleApiV1::new().translate( - &text, *google::Language::from_language_code(&lang_from)?, *google::Language::from_language_code(&lang_to)? - ) - }, - "google_api_v2" => { - google::GoogleApiV2::new().translate( - &text, *google::Language::from_language_code(&lang_from)?, *google::Language::from_language_code(&lang_to)? - ) - }, - "google_scrape" => { - google::GoogleScrape::new().translate( - &text, *google::Language::from_language_code(&lang_from)?, *google::Language::from_language_code(&lang_to)? - ) - }, - _ => { return Some(()); } - }; + let mut translator = MetaTranslator::new(tl_engine_config.name, tl_engine_config.url); + if tl_engine_config.api_key.len() > 0 { + translator.set_key(tl_engine_config.api_key); + } + let translation = translator.translate(&text, &lang_from, &lang_to); - let tl = match translatet_text { - Ok(res) => { - res.translation - }, + let tl = match translation { + Ok(res) => res.translation, Err(e) => { format!("Error when Translating: {:?}", e) } }; - if !*cancel_token.lock().unwrap(){ + if !*cancel_token.lock().unwrap() { sender.send(Message::UpdateTlLabel(tl)).ok(); } Some(()) } - fn get_sub_text(mpv: &mut Mpv) -> Result, ()> { let event: Event = match mpv.event_listen() { - Ok(ev) => { ev }, + Ok(ev) => ev, Err(..) => { return Err(()); } }; if let Event::PropertyChange { - id: _, property: Property::Unknown { - name: _, data: MpvDataType::String(string) - } - } = event { + id: _, + property: + Property::Unknown { + name: _, + data: MpvDataType::String(string), + }, + } = event + { return Ok(Some(string)); } Ok(None) } - From 8a929240a2791fc15c9c628a5d932a5c912be5e4 Mon Sep 17 00:00:00 2001 From: sdaqo Date: Thu, 30 May 2024 23:51:19 +0200 Subject: [PATCH 2/2] clippy the code --- src/app/ctxmenu/mod.rs | 3 ++- src/app/reference_dialog/mod.rs | 4 ++-- src/app/reference_dialog/translator/mod.rs | 6 +++--- src/language/translators/deeplx.rs | 10 ++++------ src/language/translators/google/googleapiv1.rs | 4 ++-- src/language/translators/google/googleapiv2.rs | 4 ++-- src/mpv.rs | 2 +- 7 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/app/ctxmenu/mod.rs b/src/app/ctxmenu/mod.rs index 28f5766..5e4895d 100644 --- a/src/app/ctxmenu/mod.rs +++ b/src/app/ctxmenu/mod.rs @@ -192,6 +192,7 @@ pub fn build_ctxmenu(window: &MpvSubsWindow) -> ContextMenu { return Inhibit(true); } + if let Some(font_desc) = font_chooser.font_desc() { let family = font_desc.family().unwrap_or_default().to_string(); let size = font_desc.size() / gtk::pango::SCALE; @@ -334,7 +335,7 @@ pub fn build_ctxmenu(window: &MpvSubsWindow) -> ContextMenu { } }; - reference_dialog.run_async(); + reference_dialog.run(); Inhibit(true) }), ), diff --git a/src/app/reference_dialog/mod.rs b/src/app/reference_dialog/mod.rs index 8d329a0..482859a 100644 --- a/src/app/reference_dialog/mod.rs +++ b/src/app/reference_dialog/mod.rs @@ -42,7 +42,7 @@ impl ReferenceDialog { } } - pub async fn run_async(&self) { - self.dialog.run_future().await; + pub fn run(&self) { + self.dialog.run(); } } diff --git a/src/app/reference_dialog/translator/mod.rs b/src/app/reference_dialog/translator/mod.rs index 4b26935..6086f77 100644 --- a/src/app/reference_dialog/translator/mod.rs +++ b/src/app/reference_dialog/translator/mod.rs @@ -51,8 +51,8 @@ impl TranslatorWidget { self.imp().lang_to_combo.remove_all(); for (full, code) in transltor.get_language_map().into_iter() { - self.imp().lang_from_combo.append(Some(&code), &full); - self.imp().lang_to_combo.append(Some(&code), &full); + self.imp().lang_from_combo.append(Some(code), full); + self.imp().lang_to_combo.append(Some(code), full); } if let Some(api_key_url) = transltor.get_api_key_url() { @@ -322,7 +322,7 @@ impl TranslatorWidget { let api_key = self.imp().api_key_field.buffer().text(); let mut translator = MetaTranslator::new(translator, Some(url)); - if api_key.len() > 0 { + if !api_key.is_empty() { translator.set_key(api_key); } let translation = translator.translate(&text, &lang_from, &lang_to); diff --git a/src/language/translators/deeplx.rs b/src/language/translators/deeplx.rs index 23ac4e2..b53f22c 100644 --- a/src/language/translators/deeplx.rs +++ b/src/language/translators/deeplx.rs @@ -35,12 +35,12 @@ impl Translator for DeeplX { let client = reqwest::blocking::Client::new(); let mut data = HashMap::<&str, &str>::new(); if in_lang_code != "auto" { - data.insert("source_lang", &in_lang_code); + data.insert("source_lang", in_lang_code); } else { data.insert("source_lang", ""); } - data.insert("target_lang", &out_lang_code); + data.insert("target_lang", out_lang_code); data.insert("text", text); let mut headers = HeaderMap::new(); @@ -54,10 +54,8 @@ impl Translator for DeeplX { if let Ok(v) = HeaderValue::from_str(&format!("DeepL-Auth-Key {}", key)) { headers.insert(reqwest::header::AUTHORIZATION, v); } - } else { - if let Ok(v) = HeaderValue::from_str(&format!("Bearer {}", key)) { - headers.insert(reqwest::header::AUTHORIZATION, v); - } + } else if let Ok(v) = HeaderValue::from_str(&format!("Bearer {}", key)) { + headers.insert(reqwest::header::AUTHORIZATION, v); } }; diff --git a/src/language/translators/google/googleapiv1.rs b/src/language/translators/google/googleapiv1.rs index 566ac4b..73d6f5f 100644 --- a/src/language/translators/google/googleapiv1.rs +++ b/src/language/translators/google/googleapiv1.rs @@ -31,8 +31,8 @@ impl Translator for GoogleApiV1 { let client = reqwest::blocking::Client::new(); let mut form = HashMap::<&str, &str>::new(); - form.insert("sl", &in_lang_code); - form.insert("tl", &out_lang_code); + form.insert("sl", in_lang_code); + form.insert("tl", out_lang_code); form.insert("q", text); let res = client diff --git a/src/language/translators/google/googleapiv2.rs b/src/language/translators/google/googleapiv2.rs index 6d8ab47..2cab818 100644 --- a/src/language/translators/google/googleapiv2.rs +++ b/src/language/translators/google/googleapiv2.rs @@ -37,8 +37,8 @@ impl Translator for GoogleApiV2 { .post(&self.url) .query(&[ ("format", "text"), - ("target", &out_lang_code), - ("source", &in_lang_code), + ("target", out_lang_code), + ("source", in_lang_code), ("key", self.key.as_ref().unwrap()), ("q", text), ]) diff --git a/src/mpv.rs b/src/mpv.rs index 88f4b62..1dd5e9a 100644 --- a/src/mpv.rs +++ b/src/mpv.rs @@ -108,7 +108,7 @@ fn tl_and_set( let lang_to = tl_engine_config.default_lang_to; let mut translator = MetaTranslator::new(tl_engine_config.name, tl_engine_config.url); - if tl_engine_config.api_key.len() > 0 { + if !tl_engine_config.api_key.is_empty() { translator.set_key(tl_engine_config.api_key); } let translation = translator.translate(&text, &lang_from, &lang_to);