mirror of
https://github.com/euzu/tuliprox.git
synced 2026-10-28 17:52:50 +01:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
@@ -228,7 +228,20 @@
|
||||
"FIRMWARE_VERSION": "Firmware Version",
|
||||
"DEVICE_TYPE": "Device Type",
|
||||
"DEVICE_UDN": "Device UDN",
|
||||
"TUNER_COUNT": "Tuner Count"
|
||||
"TUNER_COUNT": "Tuner Count",
|
||||
"SOURCE": "Source",
|
||||
"PRIORITY": "Priority",
|
||||
"LOGO_OVERRIDE": "logo Override",
|
||||
"SMART_EPG": "Smart Epg",
|
||||
"DEFAULT_EPG": "Normal Epg",
|
||||
"EPG_SMART_MATCH": "EPG Smart Match",
|
||||
"FUZZY_MATCHING": "Fuzzy Matching",
|
||||
"MATCH_THRESHOLD": "Match Threshold",
|
||||
"BEST_MATCH_THRESHOLD": "Best Match Threshold",
|
||||
"NORMALIZE_REGEX": "Normalize Regex",
|
||||
"NAME_PREFIX": "Name Prefix",
|
||||
"NAME_PREFIX_SEPARATOR":"Name Prefix Separator",
|
||||
"STRIP": "Strip"
|
||||
},
|
||||
"TABLE": {
|
||||
"EMPTY": "",
|
||||
|
||||
@@ -8,7 +8,10 @@
|
||||
|
||||
&:hover {
|
||||
fill: var(--text-color);
|
||||
cursor: pointer;
|
||||
filter: brightness(1.2);
|
||||
}
|
||||
|
||||
.svg-icon {
|
||||
width: 1.2rem;
|
||||
height: 1.2rem;
|
||||
@@ -22,6 +25,7 @@
|
||||
text-overflow: ellipsis;
|
||||
max-height: 2rem;
|
||||
max-width: var(--max-table-cell-width);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -83,7 +83,7 @@ macro_rules! config_field {
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! config_field_child {
|
||||
($config:expr, $label:expr, $body:block) => {
|
||||
($label:expr, $body:block) => {
|
||||
html! {
|
||||
<div class="tp__config-field tp__config-field__text">
|
||||
<label>{$label}</label>
|
||||
|
||||
@@ -16,7 +16,7 @@ pub fn MessagingConfigView() -> Html {
|
||||
<Card class="tp__config-view__card">
|
||||
<h1>{translate.t("LABEL.TELEGRAM")}</h1>
|
||||
{ config_field!(entry, translate.t("LABEL.BOT_TOKEN"), bot_token) }
|
||||
{ config_field_child!(entry, translate.t("LABEL.CHAT_ID"), {
|
||||
{ config_field_child!(translate.t("LABEL.CHAT_ID"), {
|
||||
html! {
|
||||
<div class="tp__config-view__tags">
|
||||
{
|
||||
@@ -102,7 +102,7 @@ pub fn MessagingConfigView() -> Html {
|
||||
html! {
|
||||
<>
|
||||
<div class="tp__messaging-config-view__header tp__config-view-page__header">
|
||||
{ config_field_child!(entry, translate.t("LABEL.NOTIFY_ON"), {
|
||||
{ config_field_child!(translate.t("LABEL.NOTIFY_ON"), {
|
||||
html! { <div class="tp__messaging-config-view__notify-on">
|
||||
{ for messaging.notify_on.iter().map(|t| html! { <Chip label={t.to_string()} /> }) }
|
||||
</div> }
|
||||
|
||||
@@ -20,7 +20,7 @@ pub fn SchedulesConfigView() -> Html {
|
||||
{ for schedules.iter().map(|entry| html! {
|
||||
<div class="tp__schedules-config-view__schedule">
|
||||
{ config_field!(entry, translate.t("LABEL.SCHEDULE"), schedule) }
|
||||
{ config_field_child!(entry, translate.t("LABEL.TARGETS"), {
|
||||
{ config_field_child!(translate.t("LABEL.TARGETS"), {
|
||||
html! { <div class="tp__config-view__tags">
|
||||
{ match entry.targets.as_ref() {
|
||||
None => html! { <Chip label={translate.t("LABEL.ALL")} /> },
|
||||
|
||||
@@ -12,7 +12,7 @@ pub fn VideoConfigView() -> Html {
|
||||
|
||||
let render_extensions = |extensions: &Vec<String>| html! {
|
||||
<Card>
|
||||
{ config_field_child!(entry, translate.t("LABEL.EXTENSIONS"), {
|
||||
{ config_field_child!(translate.t("LABEL.EXTENSIONS"), {
|
||||
html! {
|
||||
<div class="tp__config-view__tags">
|
||||
{
|
||||
@@ -31,7 +31,7 @@ pub fn VideoConfigView() -> Html {
|
||||
{ config_field_bool!(entry, translate.t("LABEL.ORGANIZE_INTO_DIRECTORIES"), organize_into_directories) }
|
||||
{ config_field_optional!(entry, translate.t("LABEL.DIRECTORY"), directory) }
|
||||
{ config_field_optional!(entry, translate.t("LABEL.EPISODE_PATTERN"), episode_pattern) }
|
||||
{ config_field_child!(entry.headers, translate.t("LABEL.HEADERS"), {
|
||||
{ config_field_child!(translate.t("LABEL.HEADERS"), {
|
||||
html! {
|
||||
<div class="tp__config-view__tags">
|
||||
<ul>
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
use crate::app::components::{Card, Chip, NoContent};
|
||||
use crate::{config_field, config_field_bool, config_field_child, config_field_optional};
|
||||
use shared::model::{EpgConfigDto, EpgSmartMatchConfigDto, EpgSourceDto};
|
||||
use yew::prelude::*;
|
||||
use yew_i18n::use_translation;
|
||||
|
||||
#[derive(Properties, PartialEq, Clone)]
|
||||
pub struct EpgConfigViewProps {
|
||||
#[prop_or_default]
|
||||
pub epg: Option<EpgConfigDto>
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
pub fn EpgConfigView(props: &EpgConfigViewProps) -> Html {
|
||||
|
||||
let translate = use_translation();
|
||||
|
||||
let render_sources = |sources: Option<&Vec<EpgSourceDto>>| {
|
||||
let render_empty_sources = || html! {
|
||||
<Card class="tp__config-view__card">
|
||||
<h1>{translate.t("LABEL.SOURCES")}</h1>
|
||||
<NoContent />
|
||||
</Card>
|
||||
};
|
||||
|
||||
if let Some(sources) = sources {
|
||||
if sources.is_empty() {
|
||||
render_empty_sources()
|
||||
} else {
|
||||
html!{
|
||||
for sources.iter().map(|entry| html! {
|
||||
<Card class="tp__config-view__card">
|
||||
<h1>{translate.t("LABEL.SOURCE")}</h1>
|
||||
{ config_field!(entry, translate.t("LABEL.URL"), url) }
|
||||
{ config_field!(entry, translate.t("LABEL.PRIORITY"), priority) }
|
||||
{ config_field_bool!(entry, translate.t("LABEL.LOGO_OVERRIDE"), logo_override) }
|
||||
</Card>
|
||||
})}
|
||||
}
|
||||
} else {
|
||||
render_empty_sources()
|
||||
}
|
||||
};
|
||||
|
||||
let render_smart_match = |epg_smart_match: Option<&EpgSmartMatchConfigDto>| {
|
||||
let render_empty_smart_match = || html! {
|
||||
<Card class="tp__config-view__card">
|
||||
<h1>{translate.t("LABEL.EPG_SMART_MATCH")}</h1>
|
||||
<NoContent />
|
||||
</Card>
|
||||
};
|
||||
|
||||
if let Some(entry) = epg_smart_match {
|
||||
html!{
|
||||
<Card class="tp__config-view__card">
|
||||
<h1>{translate.t("LABEL.EPG_SMART_MATCH")}</h1>
|
||||
{ config_field_bool!(entry, translate.t("LABEL.ENABLED"), enabled) }
|
||||
{ config_field_bool!(entry, translate.t("LABEL.FUZZY_MATCHING"), fuzzy_matching) }
|
||||
{ config_field!(entry, translate.t("LABEL.MATCH_THRESHOLD"), match_threshold) }
|
||||
{ config_field!(entry, translate.t("LABEL.BEST_MATCH_THRESHOLD"), best_match_threshold) }
|
||||
{ config_field_optional!(entry, translate.t("LABEL.NORMALIZE_REGEX"), normalize_regex) }
|
||||
{ config_field!(entry, translate.t("LABEL.NAME_PREFIX"), name_prefix) }
|
||||
{ config_field_child!(translate.t("LABEL.NAME_PREFIX_SEPARATOR"), {
|
||||
html! {
|
||||
<div class="tp__config-view__tags">
|
||||
{
|
||||
if let Some(name_prefixe_seperators) = entry.name_prefix_separator.as_ref() {
|
||||
if name_prefixe_seperators.is_empty() {
|
||||
html! {}
|
||||
} else {
|
||||
html! {
|
||||
for name_prefixe_seperators.iter().map(|value| html! {
|
||||
<Chip label={value.to_string()} />
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
html! {}
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
})}
|
||||
{ config_field_child!(translate.t("LABEL.STRIP"), {
|
||||
html! {
|
||||
<div class="tp__config-view__tags">
|
||||
{
|
||||
if let Some(strip) = entry.strip.as_ref() {
|
||||
if strip.is_empty() {
|
||||
html! {}
|
||||
} else {
|
||||
html! {
|
||||
for strip.iter().map(|value| html! {
|
||||
<Chip label={value.clone()} />
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
html! {}
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
})}
|
||||
</Card>
|
||||
}
|
||||
} else {
|
||||
render_empty_smart_match()
|
||||
}
|
||||
};
|
||||
|
||||
let render_empty = || html! {
|
||||
<div class="tp__messaging-config-view__body tp__config-view-page__body">
|
||||
{render_smart_match(None)}
|
||||
{render_sources(None)}
|
||||
</div>
|
||||
};
|
||||
|
||||
html! {
|
||||
<div class="tp__epg-config-view tp__config-view-page">
|
||||
{
|
||||
if let Some(epg) = &props.epg {
|
||||
html! {
|
||||
<div class="tp__epg-config-view__body tp__config-view-page__body">
|
||||
{render_smart_match(epg.smart_match.as_ref())}
|
||||
{render_sources(epg.sources.as_ref())}
|
||||
</div>
|
||||
}
|
||||
} else {
|
||||
{ render_empty() }
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::fmt::Display;
|
||||
use crate::app::components::popup_menu::PopupMenu;
|
||||
use crate::app::components::{convert_bool_to_chip_style, AppIcon, BatchInputContentView, Chip, HideContent, InputHeaders, InputOptions, InputTypeView, RevealContent, StagedInputView, Table, TableDefinition};
|
||||
use crate::app::components::{convert_bool_to_chip_style, AppIcon, BatchInputContentView, Chip, EpgConfigView, HideContent, InputHeaders, InputOptions, InputTypeView, RevealContent, StagedInputView, Table, TableDefinition};
|
||||
use std::rc::Rc;
|
||||
use std::str::FromStr;
|
||||
use yew::platform::spawn_local;
|
||||
@@ -117,7 +117,14 @@ pub fn InputTable(props: &InputTableProps) -> Html {
|
||||
9 => html! { dto.priority.to_string() },
|
||||
10 => html! { dto.max_connections.to_string() },
|
||||
11 => html! { dto.method.to_string() },
|
||||
12 => html! { /* TODO */ },
|
||||
12 => html_if!(dto.epg.is_some(),
|
||||
{ <RevealContent preview={ html!{ dto.epg.as_ref().map_or_else(|| html!{}, |e| html! {
|
||||
<Chip class={if e.smart_match.is_some() {"active"} else { "" }}
|
||||
label={ if e.smart_match.is_some() {translator.t("LABEL.SMART_EPG")} else { translator.t("LABEL.DEFAULT_EPG")}}
|
||||
/>
|
||||
})}}>
|
||||
<EpgConfigView epg={ dto.epg.clone() } />
|
||||
</RevealContent> }),
|
||||
13 => html! { <InputHeaders headers={dto.headers.clone()} /> },
|
||||
14 => html_if!(dto.staged.is_some(),
|
||||
{ <RevealContent preview={ html!{ dto.staged.as_ref().map_or_else(String::new, |s| s.url.clone())} }>
|
||||
|
||||
@@ -17,6 +17,7 @@ mod playlist_explorer;
|
||||
mod playlist_update_view;
|
||||
mod mapper_script_view;
|
||||
mod mapper_counter_view;
|
||||
mod epg_config_view;
|
||||
|
||||
use std::rc::Rc;
|
||||
use yew_i18n::YewI18n;
|
||||
@@ -40,6 +41,7 @@ pub use self::playlist_explorer_view::*;
|
||||
pub use self::playlist_update_view::*;
|
||||
pub use self::mapper_script_view::*;
|
||||
pub use self::mapper_counter_view::*;
|
||||
pub use self::epg_config_view::*;
|
||||
|
||||
pub fn make_tags(data: &[(bool, &str)], translate: &YewI18n) -> Vec<Rc<Tag>> {
|
||||
data.iter()
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::fmt::Display;
|
||||
use log::warn;
|
||||
use crate::error::{create_tuliprox_error_result, TuliproxError, TuliproxErrorKind};
|
||||
|
||||
@@ -10,6 +11,22 @@ pub enum EpgNamePrefix {
|
||||
Prefix(String),
|
||||
}
|
||||
|
||||
impl EpgNamePrefix {
|
||||
const IGNORE: &'static str = "Ignore";
|
||||
const SUFFIX: &'static str = "Suffix";
|
||||
const PREFIX: &'static str = "Prefix";
|
||||
}
|
||||
|
||||
impl Display for EpgNamePrefix {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Ignore => write!(f, "{}", Self::IGNORE),
|
||||
Self::Suffix(s) => write!(f, "{}({s})", Self::SUFFIX),
|
||||
Self::Prefix(s) => write!(f, "{}({s})", Self::PREFIX),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct EpgSmartMatchConfigDto {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::error::{TuliproxError, TuliproxErrorKind};
|
||||
use crate::model::EpgConfigDto;
|
||||
use crate::utils::{default_as_true, get_base_url_from_str, get_credentials_from_url_str, get_trimmed_string, sanitize_sensitive_info, trim_last_slash};
|
||||
use crate::utils::{default_as_true, get_credentials_from_url_str, get_trimmed_string, sanitize_sensitive_info, trim_last_slash};
|
||||
use crate::{check_input_credentials, check_input_connections, create_tuliprox_error_result, handle_tuliprox_error_result_list, info_err};
|
||||
use enum_iterator::Sequence;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
@@ -323,16 +323,14 @@ impl ConfigInputDto {
|
||||
|
||||
if username.is_none() || password.is_none() || base_url.is_none(){
|
||||
Err(format!("auto_epg is enabled for input {}, but no credentials could be extracted", self.name))
|
||||
} else if base_url.is_some() {
|
||||
let provider_epg_url = format!("{}/xmltv.php?username={}&password={}",
|
||||
trim_last_slash(&base_url.unwrap_or_default()),
|
||||
username.unwrap_or_default(),
|
||||
password.unwrap_or_default());
|
||||
Ok(provider_epg_url)
|
||||
} else {
|
||||
if base_url.is_some() {
|
||||
let provider_epg_url = format!("{}/xmltv.php?username={}&password={}",
|
||||
trim_last_slash(&base_url.unwrap_or_default()),
|
||||
username.unwrap_or_default(),
|
||||
password.unwrap_or_default());
|
||||
Ok(provider_epg_url)
|
||||
} else {
|
||||
Err(format!("auto_epg is enabled for input {}, but url could not be parsed {}", self.name, sanitize_sensitive_info(&self.url)))
|
||||
}
|
||||
Err(format!("auto_epg is enabled for input {}, but url could not be parsed {}", self.name, sanitize_sensitive_info(&self.url)))
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user