Skip to content

Commit

Permalink
export group
Browse files Browse the repository at this point in the history
  • Loading branch information
Fredolx committed Oct 28, 2024
1 parent 3ddcbc7 commit 15d09c4
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 16 deletions.
11 changes: 9 additions & 2 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use anyhow::Error;
use types::{Channel, ChannelHttpHeaders, CustomChannel, CustomChannelExtraData, Filters, Group, IdName, Settings, Source};
use types::{Channel, ChannelHttpHeaders, CustomChannel, CustomChannelExtraData, ExportedGroup, Filters, Group, IdName, Settings, Source};

pub mod log;
pub mod m3u;
Expand Down Expand Up @@ -51,7 +51,8 @@ pub fn run() {
add_custom_group,
delete_custom_group,
group_not_empty,
group_exists
group_exists,
share_custom_group
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
Expand Down Expand Up @@ -219,3 +220,9 @@ fn group_exists(name: Option<String>, source_id: i64) -> Result<bool, String> {
sql::group_exists(name, source_id).map_err(map_err_frontend)
}

#[tauri::command(async)]
fn share_custom_group(group: Channel) -> Result<(), String> {
share::share_custom_group(group).map_err(map_err_frontend)
}


2 changes: 1 addition & 1 deletion src-tauri/src/m3u.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ fn get_channel_from_lines(first: String, mut second: String, source_id: i64) ->
image: image.map(|x| x.trim().to_string()),
url: Some(second.clone()),
media_type: get_media_type(second),
source_id: source_id,
source_id: Some(source_id),
series_id: None,
group_id: None,
favorite: false,
Expand Down
33 changes: 28 additions & 5 deletions src-tauri/src/share.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
use anyhow::Context;
use anyhow::Result;
use directories::UserDirs;
use serde::Serialize;
use crate::types::CustomChannel;
use crate::types::ExportedGroup;
use crate::types::Group;
use crate::{sql, types::Channel};

const CHANNEL_SHARE_EXTENSION: &str = ".otv";
const PLAYLIST_SHARE_EXTENSION: &str = ".otvp";
const GROUP_SHARE_EXTENSION: &str = ".ovtg";

pub fn share_custom_channel(channel: Channel) -> Result<()> {
let channel = get_custom_channel(channel)?;
let path = get_download_path(channel.data.id.context("No id on channel?")?)?;
let path = get_download_path(channel.data.id.context("No id on channel?")?,
CHANNEL_SHARE_EXTENSION)?;
serialize_to_file(channel, path)
}

fn get_download_path(id: i64) -> Result<String> {
fn get_download_path(id: i64, extension: &str) -> Result<String> {
let path = UserDirs::new().context("No user dirs?")?;
let path = path.download_dir().context("No downloads folder")?;
let mut path = path.to_path_buf();
path.push(format!("{id}{CHANNEL_SHARE_EXTENSION}"));
path.push(format!("{id}{extension}"));
Ok(path.to_string_lossy().to_string())
}

Expand All @@ -28,8 +33,26 @@ fn get_custom_channel(channel: Channel) -> Result<CustomChannel> {
})
}

fn serialize_to_file(channel: CustomChannel, path: String) -> Result<()> {
let data = serde_json::to_string(&channel)?;
fn serialize_to_file<T: Serialize>(obj: T, path: String) -> Result<()> {
let data = serde_json::to_string(&obj)?;
std::fs::write(path, data)?;
Ok(())
}

pub fn share_custom_group(group: Channel) -> Result<()> {
let to_export = ExportedGroup {
group: Group {
id: group.id,
image: group.image,
name: group.name,
source_id: None
},
channels: sql::get_custom_channels_by_group(group.id.context("No group id")?)?
};
let path = get_download_path(
group.id.context("No group id?")?,
GROUP_SHARE_EXTENSION
)?;
serialize_to_file(to_export, path)?;
Ok(())
}
41 changes: 41 additions & 0 deletions src-tauri/src/sql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -840,6 +840,47 @@ pub fn group_not_empty(id: i64) -> Result<bool> {
.is_some())
}

pub fn get_custom_channels_by_group(id: i64) -> Result<Vec<CustomChannel>> {
let sql = get_conn()?;
let result = sql
.prepare(
r#"
SELECT c.name, c.image, c.url, c.media_type, ch.referrer, ch.user_agent, ch.http_origin, ch.ignore_ssl
FROM channels c
LEFT JOIN channel_http_headers ch on ch.channel_id = c.id
WHERE c.group_id = ?
"#)?
.query_map(params![id],row_to_custom_channel)?
.filter_map(Result::ok)
.collect();
Ok(result)
}

fn row_to_custom_channel(row: &Row) -> Result<CustomChannel, rusqlite::Error> {
Ok(CustomChannel {
data: Channel {
name: row.get("name")?,
image: row.get("image")?,
url: row.get("url")?,
media_type: row.get("media_type")?,
favorite: false,
group_id: None,
group: None,
id: None,
series_id: None,
source_id: None
},
headers: Some(ChannelHttpHeaders {
http_origin: row.get("http_origin")?,
ignore_ssl: row.get("ignore_ssl")?,
referrer: row.get("referrer")?,
user_agent: row.get("user_agent")?,
channel_id: None,
id: None
})
})
}

#[cfg(test)]
mod test_sql {
use std::collections::HashMap;
Expand Down
19 changes: 17 additions & 2 deletions src-tauri/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@ use serde::{Deserialize, Serialize};

#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
pub struct Channel {
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<i64>,
pub name: String,
pub url: Option<String>,
pub group: Option<String>,
pub image: Option<String>,
pub media_type: u8,
pub source_id: i64,
#[serde(skip_serializing_if = "Option::is_none")]
pub source_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub series_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub group_id: Option<i64>,
pub favorite: bool,
}
Expand Down Expand Up @@ -48,7 +52,9 @@ pub struct Filters {

#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
pub struct ChannelHttpHeaders {
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub channel_id: Option<i64>,
pub referrer: Option<String>,
pub user_agent: Option<String>,
Expand All @@ -64,10 +70,12 @@ pub struct CustomChannel {

#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
pub struct Group {
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<i64>,
pub name: String,
pub image: Option<String>,
pub source_id: i64
#[serde(skip_serializing_if = "Option::is_none")]
pub source_id: Option<i64>
}

#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
Expand All @@ -81,3 +89,10 @@ pub struct CustomChannelExtraData {
pub headers: Option<ChannelHttpHeaders>,
pub group: Option<Group>
}

#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
pub struct ExportedGroup {
pub group: Group,
pub channels: Vec<CustomChannel>
}

4 changes: 2 additions & 2 deletions src-tauri/src/xtream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ fn convert_xtream_live_to_channel(
.map(|x| x.trim().to_string()),
media_type: stream_type.clone(),
name: stream.name.context("No name")?.trim().to_string(),
source_id: source.id.unwrap(),
source_id: source.id,
url: if stream_type == media_type::SERIE {
Some(stream.series_id.context("no series id")?.to_string())
} else {
Expand Down Expand Up @@ -254,7 +254,7 @@ fn episode_to_channel(episode: XtreamEpisode, source: &Source, series_id: i64) -
image: episode.info.map(|info| info.movie_image).unwrap_or(None),
media_type: media_type::MOVIE,
name: episode.title.trim().to_string(),
source_id: source.id.context("Invalid ID")?,
source_id: source.id,
url: Some(get_url(
episode.id,
&source,
Expand Down
2 changes: 1 addition & 1 deletion src/app/channel-tile/channel-tile.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
</button>
<button [hidden]="isMovie()" mat-menu-item (click)="record()">Record</button>
<button [hidden]="!isCustomChannel()" mat-menu-item (click)="edit()">Edit</button>
<button [hidden]="!isCustomChannel() || this.channel?.media_type == mediaTypeEnum.group" mat-menu-item (click)="share()">Share</button>
<button [hidden]="!isCustomChannel()" mat-menu-item (click)="share()">Share</button>
<button [hidden]="!isCustomChannel()" mat-menu-item (click)="delete()">Delete</button>
</ng-template>
</mat-menu>
13 changes: 10 additions & 3 deletions src/app/channel-tile/channel-tile.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,16 @@ export class ChannelTileComponent {
}

share() {
this.memory.tryIPC(`Successfully exported channel to Downloads/${this.channel?.id}.otv`,
"Failed to export channel",
() => invoke('share_custom_channel', { channel: this.channel }))
if (this.channel?.media_type == MediaType.group) {
this.memory.tryIPC(`Successfully exported category to Downloads/${this.channel?.id}.otvg`,
"Failed to export channel",
() => invoke('share_custom_group', { group: this.channel }));
}
else {
this.memory.tryIPC(`Successfully exported channel to Downloads/${this.channel?.id}.otv`,
"Failed to export channel",
() => invoke('share_custom_channel', { channel: this.channel }));
}
}

async delete() {
Expand Down

0 comments on commit 15d09c4

Please sign in to comment.