mirror of
https://github.com/euzu/tuliprox.git
synced 2026-10-09 01:12:23 +02:00
epg fix (#537)
New Features Aliases can now be individually enabled or disabled to manage active sources. Bug Fixes Improved channel identification accuracy in XMLTV outputs. Style Disabled aliases display with strikethrough text for better visibility.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
# Changelog
|
||||
|
||||
|
||||
## 3.3.0 (2026-01-03)
|
||||
|
||||
## ⚠️ Breaking Changes
|
||||
|
||||
Generated
+3
-3
@@ -1156,7 +1156,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "frontend"
|
||||
version = "3.2.53"
|
||||
version = "3.2.54"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
@@ -4015,7 +4015,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "shared"
|
||||
version = "3.2.53"
|
||||
version = "3.2.54"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bitflags 2.10.0",
|
||||
@@ -4592,7 +4592,7 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||
|
||||
[[package]]
|
||||
name = "tuliprox"
|
||||
version = "3.2.53"
|
||||
version = "3.2.54"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"async-compression",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "tuliprox"
|
||||
version = "3.2.53"
|
||||
version = "3.2.54"
|
||||
edition = "2021"
|
||||
rust-version = "1.89.0"
|
||||
|
||||
|
||||
@@ -197,7 +197,7 @@ async fn serve_epg_with_rewrites(
|
||||
let (user_start, user_stop) = apply_user_offset(programme.start, programme.stop, epg_processing_options.offset_minutes);
|
||||
elem.push_attribute(("start", format_xmltv_time_utc(user_start).as_str()));
|
||||
elem.push_attribute(("stop", format_xmltv_time_utc(user_stop).as_str()));
|
||||
elem.push_attribute(("channel", &programme.channel[..]));
|
||||
elem.push_attribute(("channel", channel.id.as_ref()));
|
||||
continue_on_err!(writer.write_event_async(Event::Start(elem)).await);
|
||||
|
||||
if let Some(title) = &programme.title {
|
||||
@@ -267,18 +267,18 @@ fn apply_user_offset(start: i64, stop: i64, offset_minutes: i32) -> (i64, i64) {
|
||||
(user_start, user_end)
|
||||
}
|
||||
|
||||
fn from_programme(stream_id: &Arc<str>, programme: &EpgProgramme, epg_processing_options: &EpgProcessingOptions) -> ShortEpgDto {
|
||||
fn from_programme(stream_id: &Arc<str>, epg_id: &Arc<str>, programme: &EpgProgramme, epg_processing_options: &EpgProcessingOptions) -> ShortEpgDto {
|
||||
let (user_start, user_end) = apply_user_offset(programme.start, programme.stop, epg_processing_options.offset_minutes);
|
||||
|
||||
ShortEpgDto {
|
||||
id: Arc::clone(stream_id),
|
||||
epg_id: Arc::clone(&programme.channel),
|
||||
epg_id: Arc::clone(epg_id),
|
||||
title: programme.title.as_ref().map_or_else(String::new, ToString::to_string),
|
||||
lang: String::new(),
|
||||
start: format_xmltv_time(user_start),
|
||||
end: format_xmltv_time(user_end),
|
||||
description: programme.desc.as_ref().map_or_else(String::new, ToString::to_string),
|
||||
channel_id: Arc::clone(&programme.channel),
|
||||
channel_id: Arc::clone(epg_id),
|
||||
start_timestamp: user_start.to_string(),
|
||||
stop_timestamp: user_end.to_string(),
|
||||
stream_id: Arc::clone(stream_id),
|
||||
@@ -300,9 +300,9 @@ pub async fn serve_short_epg(
|
||||
let epg_processing_options = get_epg_processing_options(app_state, user, target);
|
||||
ShortEpgResultDto {
|
||||
epg_listings: if limit > 0 {
|
||||
epg_channel.get_programme_with_limit(limit).iter().map(|p| from_programme(&stream_id, p, &epg_processing_options)).collect()
|
||||
epg_channel.get_programme_with_limit(limit).iter().map(|p| from_programme(&stream_id, channel_id, p, &epg_processing_options)).collect()
|
||||
} else {
|
||||
epg_channel.programmes.iter().map(|p| from_programme(&stream_id, p, &epg_processing_options)).collect()
|
||||
epg_channel.programmes.iter().map(|p| from_programme(&stream_id, channel_id, p, &epg_processing_options)).collect()
|
||||
},
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -67,7 +67,7 @@ impl ActiveProviderManager {
|
||||
}
|
||||
|
||||
fn get_config_inputs(cfg: &AppConfig) -> Vec<Arc<ConfigInput>> {
|
||||
cfg.sources.load().inputs.iter().map(Arc::clone).collect()
|
||||
cfg.sources.load().inputs.iter().filter(|i| i.enabled).map(Arc::clone).collect()
|
||||
}
|
||||
|
||||
fn get_grace_options(cfg: &AppConfig) -> (u64, u64) {
|
||||
|
||||
@@ -247,7 +247,7 @@ impl MultiProviderLineup {
|
||||
) -> Self {
|
||||
let input_connection = get_or_create_provider_connection(provider_connections, &cfg_input.name);
|
||||
let mut inputs = vec![ProviderConfigWrapper::new(ProviderConfig::new(cfg_input, input_connection, Arc::clone(connection_change)))];
|
||||
if let Some(aliases) = &cfg_input.aliases {
|
||||
if let Some(aliases) = cfg_input.get_enabled_aliases() {
|
||||
for alias in aliases {
|
||||
let alias_connection = get_or_create_provider_connection(provider_connections, &alias.name);
|
||||
inputs.push(ProviderConfigWrapper::new(ProviderConfig::new_alias(
|
||||
@@ -540,7 +540,7 @@ impl ProviderLineupManager {
|
||||
event_manager.send_provider_event(name, connections);
|
||||
});
|
||||
|
||||
if cfg_input.aliases.as_ref().is_some_and(|a| !a.is_empty()) {
|
||||
if cfg_input.has_enabled_aliases() {
|
||||
ProviderLineup::Multi(MultiProviderLineup::new(cfg_input, provider_connections, &on_connection_change))
|
||||
} else {
|
||||
let connection = get_or_create_provider_connection(provider_connections, &cfg_input.name);
|
||||
@@ -575,6 +575,7 @@ impl ProviderLineupManager {
|
||||
};
|
||||
|
||||
if a_alias.max_connections != b_alias.max_connections
|
||||
|| a_alias.enabled != b_alias.enabled
|
||||
|| a_alias.priority != b_alias.priority
|
||||
|| a_alias.username != b_alias.username
|
||||
|| a_alias.password != b_alias.password
|
||||
|
||||
@@ -1364,6 +1364,7 @@ async fn patch_source_yml_add_alias(
|
||||
priority: 0,
|
||||
max_connections: 1,
|
||||
exp_date,
|
||||
enabled: true,
|
||||
};
|
||||
|
||||
input.upsert_alias(alias)?;
|
||||
@@ -1632,6 +1633,7 @@ fn apply_sources_yml_patches(
|
||||
priority: 0,
|
||||
max_connections: 1,
|
||||
exp_date: *exp_date,
|
||||
enabled: true,
|
||||
};
|
||||
alias.prepare(next_index, &input_type)?;
|
||||
aliases.push(alias);
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
use crate::model::{macros, EpgConfig, PanelApiConfig};
|
||||
use crate::repository::get_csv_file_path;
|
||||
use chrono::Utc;
|
||||
use log::warn;
|
||||
use shared::check_input_credentials;
|
||||
use shared::error::TuliproxError;
|
||||
use shared::model::{ConfigInputAliasDto, ConfigInputDto, ConfigInputOptionsDto, InputFetchMethod, InputType, StagedInputDto};
|
||||
use shared::utils::{get_credentials_from_url, Internable};
|
||||
use shared::{check_input_connections, info_err_res, write_if_some};
|
||||
use shared::check_input_credentials;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use url::Url;
|
||||
use crate::repository::get_csv_file_path;
|
||||
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -105,6 +105,7 @@ pub struct ConfigInputAlias {
|
||||
pub priority: i16,
|
||||
pub max_connections: u16,
|
||||
pub exp_date: Option<i64>,
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
macros::from_impl!(ConfigInputAlias);
|
||||
@@ -119,6 +120,7 @@ impl From<&ConfigInputAliasDto> for ConfigInputAlias {
|
||||
priority: dto.priority,
|
||||
max_connections: dto.max_connections,
|
||||
exp_date: dto.exp_date,
|
||||
enabled: dto.enabled,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -165,6 +167,15 @@ impl ConfigInput {
|
||||
self.enabled = false;
|
||||
}
|
||||
|
||||
if let Some(aliases) = &mut self.aliases {
|
||||
for alias in aliases {
|
||||
if is_input_expired(alias.exp_date) {
|
||||
warn!("Account {} expired for provider: {}", alias.username.as_ref().map_or("?", |s| s.as_str()), alias.name);
|
||||
alias.enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(panel_api) = &mut self.panel_api {
|
||||
panel_api.prepare()?;
|
||||
}
|
||||
@@ -210,15 +221,20 @@ impl ConfigInput {
|
||||
}
|
||||
|
||||
if !aliases.is_empty() {
|
||||
let mut first = aliases.remove(0);
|
||||
self.id = first.id;
|
||||
self.username = first.username.take();
|
||||
self.password = first.password.take();
|
||||
self.url = first.url.trim().to_string();
|
||||
self.max_connections = first.max_connections;
|
||||
self.priority = first.priority;
|
||||
if self.name.is_empty() {
|
||||
self.name.clone_from(&first.name);
|
||||
if let Some(index) = aliases.iter().position(|alias| alias.enabled) {
|
||||
let mut first = aliases.remove(index);
|
||||
self.id = first.id;
|
||||
self.username = first.username.take();
|
||||
self.password = first.password.take();
|
||||
self.url = first.url.trim().to_string();
|
||||
self.max_connections = first.max_connections;
|
||||
self.priority = first.priority;
|
||||
self.enabled = first.enabled;
|
||||
if self.name.is_empty() {
|
||||
self.name.clone_from(&first.name);
|
||||
}
|
||||
} else {
|
||||
self.enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -254,6 +270,23 @@ impl ConfigInput {
|
||||
cache_duration_seconds: self.cache_duration_seconds,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_enabled_aliases(&self) -> bool {
|
||||
self.aliases
|
||||
.as_ref()
|
||||
.is_some_and(|aliases| aliases.iter().any(|a| a.enabled))
|
||||
}
|
||||
|
||||
pub fn get_enabled_aliases(&self) -> Option<Vec<&ConfigInputAlias>> {
|
||||
self.aliases.as_ref().map_or(None, |aliases| {
|
||||
let result: Vec<_> = aliases.iter().filter(|alias| alias.enabled).collect();
|
||||
if result.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
macros::from_impl!(ConfigInput);
|
||||
|
||||
@@ -194,7 +194,7 @@ fn filter_channels_and_programmes(
|
||||
) {
|
||||
let mut prog_map: HashMap<Arc<str>, Vec<EpgProgramme>> = HashMap::new();
|
||||
for prog in programmes.drain(..) {
|
||||
prog_map.entry(prog.channel.clone()).or_default().push(prog);
|
||||
prog_map.entry(prog.get_transient_channel_id().clone()).or_default().push(prog);
|
||||
}
|
||||
|
||||
for channel in channels.iter_mut() {
|
||||
|
||||
@@ -23,6 +23,7 @@ const FIELD_NAME: &str = "name";
|
||||
const FIELD_USERNAME: &str = "username";
|
||||
const FIELD_PASSWORD: &str = "password";
|
||||
const FIELD_EXP_DATE: &str = "exp_date";
|
||||
const FIELD_ENABLED: &str = "enabled";
|
||||
const FIELD_UNKNOWN: &str = "?";
|
||||
const DEFAULT_COLUMNS: &[&str] = &[
|
||||
FIELD_URL,
|
||||
@@ -32,6 +33,7 @@ const DEFAULT_COLUMNS: &[&str] = &[
|
||||
FIELD_USERNAME,
|
||||
FIELD_PASSWORD,
|
||||
FIELD_EXP_DATE,
|
||||
FIELD_ENABLED
|
||||
];
|
||||
const CSV_EXTENSION: &str = ".csv";
|
||||
|
||||
@@ -108,6 +110,16 @@ fn csv_assign_mandatory_fields(alias: &mut ConfigInputAliasDto, input_type: Inpu
|
||||
}
|
||||
}
|
||||
|
||||
fn str_to_bool(val: &str) -> bool {
|
||||
if val.is_empty() || val == "1" {
|
||||
return true;
|
||||
}
|
||||
if val == "0" || val.eq_ignore_ascii_case("f") || val.eq_ignore_ascii_case("false") {
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn csv_assign_config_input_column(
|
||||
config_input: &mut ConfigInputAliasDto,
|
||||
header: &str,
|
||||
@@ -143,6 +155,9 @@ fn csv_assign_config_input_column(
|
||||
None
|
||||
});
|
||||
}
|
||||
FIELD_ENABLED => {
|
||||
config_input.enabled = str_to_bool(value);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -180,6 +195,7 @@ pub fn csv_read_inputs_from_reader(
|
||||
FIELD_USERNAME => FIELD_USERNAME,
|
||||
FIELD_PASSWORD => FIELD_PASSWORD,
|
||||
FIELD_EXP_DATE => FIELD_EXP_DATE,
|
||||
FIELD_ENABLED => FIELD_ENABLED,
|
||||
_ => {
|
||||
error!("Field {s} is unsupported for csv input");
|
||||
FIELD_UNKNOWN
|
||||
@@ -199,6 +215,7 @@ pub fn csv_read_inputs_from_reader(
|
||||
priority: 0,
|
||||
max_connections: 1,
|
||||
exp_date: None,
|
||||
enabled: true,
|
||||
};
|
||||
|
||||
let columns: Vec<&str> = line.split(CSV_SEPARATOR).collect();
|
||||
@@ -276,6 +293,8 @@ async fn csv_write_input_to_path(
|
||||
content.push(CSV_SEPARATOR);
|
||||
content.push_str(FIELD_URL);
|
||||
content.push(CSV_SEPARATOR);
|
||||
content.push_str(FIELD_ENABLED);
|
||||
content.push(CSV_SEPARATOR);
|
||||
content.push_str(FIELD_MAX_CON);
|
||||
content.push(CSV_SEPARATOR);
|
||||
content.push_str(FIELD_PRIO);
|
||||
@@ -292,6 +311,8 @@ async fn csv_write_input_to_path(
|
||||
content.push(CSV_SEPARATOR);
|
||||
content.push_str(&alias.url);
|
||||
content.push(CSV_SEPARATOR);
|
||||
content.push_str(if alias.enabled { "1" } else {"0"} );
|
||||
content.push(CSV_SEPARATOR);
|
||||
content.push_str(&alias.max_connections.to_string());
|
||||
content.push(CSV_SEPARATOR);
|
||||
content.push_str(&alias.priority.to_string());
|
||||
@@ -352,6 +373,7 @@ pub async fn csv_patch_batch_append(
|
||||
priority: 0,
|
||||
max_connections: 1,
|
||||
exp_date,
|
||||
enabled: true,
|
||||
};
|
||||
aliases.push(alias);
|
||||
|
||||
|
||||
@@ -107,11 +107,7 @@ pub fn epg_write_file(target: &ConfigTarget, epg: &Epg, path: &Path, playlist: O
|
||||
desc.clone_from(&child.value);
|
||||
}
|
||||
}
|
||||
channel.programmes.push(EpgProgramme {
|
||||
start: start_time,
|
||||
stop: stop_time,
|
||||
channel: Arc::clone(channel_id), title, desc
|
||||
});
|
||||
channel.programmes.push(EpgProgramme::new_all(start_time, stop_time, Arc::clone(channel_id), title, desc));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -628,7 +628,7 @@ async fn persist_single_template(prefix: &str, kind: Option<&MsgKind>, template:
|
||||
if tokio::fs::metadata(template).await.is_ok() {
|
||||
return Url::from_file_path(template)
|
||||
.map(|u| u.to_string())
|
||||
.map_err(|()| info_err!("Failed to convert path to file URL: {}", template));
|
||||
.map_err(|()| info_err!("Failed to convert path to file URL: {template}"));
|
||||
}
|
||||
|
||||
// It's a raw string, persist it
|
||||
|
||||
@@ -1103,6 +1103,12 @@ pub fn is_uri(url: &str) -> bool {
|
||||
.is_ok_and(|u| u.scheme().eq_ignore_ascii_case("file") || u.scheme().eq_ignore_ascii_case("http") || u.scheme().eq_ignore_ascii_case("https"))
|
||||
}
|
||||
|
||||
pub fn is_uri(url: &str) -> bool {
|
||||
Url::parse(url)
|
||||
.map(|u| u.scheme().eq_ignore_ascii_case("file") || u.scheme().eq_ignore_ascii_case("http") || u.scheme().eq_ignore_ascii_case("https"))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use shared::utils::{get_base_url_from_str, replace_url_extension, sanitize_sensitive_info};
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
[package]
|
||||
name = "frontend"
|
||||
version = "3.2.53"
|
||||
version = "3.2.54"
|
||||
edition = "2021"
|
||||
rust-version = "1.89.0"
|
||||
|
||||
[dependencies]
|
||||
shared = { version = "3.2.53", path = "../shared" }
|
||||
shared = { version = "3.2.54", path = "../shared" }
|
||||
chrono = "0"
|
||||
yew = "0.21"
|
||||
yew-router = "0.18"
|
||||
|
||||
@@ -62,6 +62,12 @@ $form-width: 480px;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
}
|
||||
|
||||
&-content{
|
||||
.inactive {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tp__form-list__toolbar {
|
||||
|
||||
@@ -6,6 +6,7 @@ use std::str::FromStr;
|
||||
use yew::platform::spawn_local;
|
||||
use yew::prelude::*;
|
||||
use yew_i18n::use_translation;
|
||||
use shared::concat_string;
|
||||
use shared::error::{info_err_res, TuliproxError};
|
||||
use shared::model::{ConfigInputAliasDto, ConfigInputDto, SortOrder};
|
||||
use crate::app::components::menu_item::MenuItem;
|
||||
@@ -149,7 +150,7 @@ pub fn InputTable(props: &InputTableProps) -> Html {
|
||||
_ => html! {""},
|
||||
}
|
||||
},
|
||||
InputRow::Alias(alias, dto) => {
|
||||
InputRow::Alias(alias, _dto) => {
|
||||
match col {
|
||||
0 => {
|
||||
let popup_onclick = popup_onclick.clone();
|
||||
@@ -162,8 +163,9 @@ pub fn InputTable(props: &InputTableProps) -> Html {
|
||||
}
|
||||
}
|
||||
1 => html! {
|
||||
<Chip class={ format!("{} tp__input-table__alias", convert_bool_to_chip_style(dto.enabled).map_or_else(String::new, |s| if s == "active" { "alias".to_string() } else {s} )) }
|
||||
label={translator.t("LABEL.ALIAS")} />
|
||||
<Chip class={format!("{} tp__input-table__alias", convert_bool_to_chip_style(alias.enabled).map_or("alias", |s| if s == "active" { "alias" } else {"inactive"} )) }
|
||||
label={if alias.enabled {translator.t("LABEL.ALIAS")} else { translator.t("LABEL.DISABLED")} }
|
||||
/>
|
||||
},
|
||||
2 => html! { alias.name.as_ref() },
|
||||
4 => html! { alias.url.as_str() },
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::sync::Arc;
|
||||
use crate::app::components::{Card, TextButton};
|
||||
use crate::{edit_field_date, edit_field_number_i16, edit_field_number_u16, edit_field_text, edit_field_text_option, generate_form_reducer};
|
||||
use crate::{edit_field_bool, edit_field_date, edit_field_number_i16, edit_field_number_u16, edit_field_text, edit_field_text_option, generate_form_reducer};
|
||||
use shared::model::ConfigInputAliasDto;
|
||||
use yew::{function_component, html, use_reducer, Callback, Html, Properties, UseReducerHandle};
|
||||
use yew_i18n::use_translation;
|
||||
@@ -13,11 +13,13 @@ const LABEL_PASSWORD: &str = "LABEL.PASSWORD";
|
||||
const LABEL_PRIORITY: &str = "LABEL.PRIORITY";
|
||||
const LABEL_MAX_CONNECTIONS: &str = "LABEL.MAX_CONNECTIONS";
|
||||
const LABEL_EXP_DATE: &str = "LABEL.EXP_DATE";
|
||||
const LABEL_ENABLED: &str = "LABEL.ENABLED";
|
||||
|
||||
generate_form_reducer!(
|
||||
state: AliasFormState { form: ConfigInputAliasDto },
|
||||
action_name: AliasFormAction,
|
||||
fields {
|
||||
Enabled => enabled: bool,
|
||||
Name => name: Arc<str>,
|
||||
Url => url: String,
|
||||
Username => username: Option<String>,
|
||||
@@ -51,6 +53,7 @@ pub fn AliasItemForm(props: &AliasItemFormProps) -> Html {
|
||||
priority: 0,
|
||||
max_connections: 1,
|
||||
exp_date: None,
|
||||
enabled: true,
|
||||
}),
|
||||
modified: false,
|
||||
}
|
||||
@@ -76,6 +79,7 @@ pub fn AliasItemForm(props: &AliasItemFormProps) -> Html {
|
||||
|
||||
html! {
|
||||
<Card class="tp__config-view__card tp__item-form">
|
||||
{ edit_field_bool!(form_state, translate.t(LABEL_ENABLED), enabled, AliasFormAction::Enabled) }
|
||||
{ edit_field_text!(form_state, translate.t(LABEL_ALIAS_NAME), name, AliasFormAction::Name) }
|
||||
{ edit_field_text!(form_state, translate.t(LABEL_URL), url, AliasFormAction::Url) }
|
||||
<div class="tp__config-view__cols-2">
|
||||
|
||||
@@ -510,7 +510,7 @@ pub fn ConfigInputView(props: &ConfigInputViewProps) -> Html {
|
||||
onclick={handle_edit_alias_list_item.clone()}/>
|
||||
</div>
|
||||
<div class="tp__form-list__item-content">
|
||||
<span><strong>{&alias.name}</strong>{" - "}{&alias.url}</span>
|
||||
<span class={if alias.enabled {""} else {"inactive"}}><strong>{&alias.name}</strong>{" - "}{&alias.url}</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "shared"
|
||||
version = "3.2.53"
|
||||
version = "3.2.54"
|
||||
edition = "2021"
|
||||
rust-version = "1.89.0"
|
||||
|
||||
|
||||
@@ -254,12 +254,10 @@ pub struct ConfigInputAliasDto {
|
||||
pub priority: i16,
|
||||
#[serde(default)]
|
||||
pub max_connections: u16,
|
||||
#[serde(
|
||||
default,
|
||||
deserialize_with = "deserialize_timestamp",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
#[serde(default, deserialize_with = "deserialize_timestamp", skip_serializing_if = "Option::is_none")]
|
||||
pub exp_date: Option<i64>,
|
||||
#[serde(default = "default_as_true", skip_serializing_if = "is_true")]
|
||||
pub enabled: bool,
|
||||
|
||||
}
|
||||
|
||||
|
||||
+17
-1
@@ -90,7 +90,14 @@ pub struct EpgProgramme {
|
||||
pub title: Option<Arc<str>>,
|
||||
pub desc: Option<Arc<str>>,
|
||||
#[serde(skip)]
|
||||
pub channel: Arc<str>,
|
||||
channel: Arc<str>,
|
||||
}
|
||||
|
||||
impl EpgProgramme {
|
||||
// the channel_id is only available when read from xml file, reading from db do not return any epg_id
|
||||
pub fn get_transient_channel_id(&self) -> &Arc<str> {
|
||||
&self.channel
|
||||
}
|
||||
}
|
||||
|
||||
impl EpgProgramme {
|
||||
@@ -103,4 +110,13 @@ impl EpgProgramme {
|
||||
desc: None,
|
||||
}
|
||||
}
|
||||
pub fn new_all(start: i64, stop: i64, channel: Arc<str>, title: Option<Arc<str>>, desc: Option<Arc<str>>) -> Self {
|
||||
Self {
|
||||
start,
|
||||
stop,
|
||||
channel,
|
||||
title,
|
||||
desc,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user