Skip to content

Commit

Permalink
finish custom channels
Browse files Browse the repository at this point in the history
  • Loading branch information
Fredolx committed Oct 25, 2024
1 parent ccc4958 commit cb012b9
Show file tree
Hide file tree
Showing 26 changed files with 513 additions and 57 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 26 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, Filters, Settings, Source};
use types::{Channel, ChannelHttpHeaders, CustomChannel, Filters, Settings, Source};

pub mod m3u;
pub mod media_type;
Expand Down Expand Up @@ -37,7 +37,11 @@ pub fn run() {
refresh_all,
get_enabled_sources,
toggle_source,
delete_database
delete_database,
add_custom_channel,
get_channel_headers,
edit_custom_channel,
delete_custom_channel
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
Expand Down Expand Up @@ -142,3 +146,23 @@ fn toggle_source(value: bool, source_id: i64) -> Result<(), String> {
fn delete_database() -> Result<(), String> {
sql::delete_database().map_err(map_err_frontend)
}

#[tauri::command(async)]
fn add_custom_channel(channel: CustomChannel) -> Result<(), String> {
sql::add_custom_channel(channel).map_err(map_err_frontend)
}

#[tauri::command(async)]
fn edit_custom_channel(channel: CustomChannel) -> Result<(), String> {
sql::edit_custom_channel(channel).map_err(map_err_frontend)
}

#[tauri::command(async)]
fn delete_custom_channel(id: i64) -> Result<(), String> {
sql::delete_custom_channel(id).map_err(map_err_frontend)
}

#[tauri::command(async)]
fn get_channel_headers(id: i64) -> Result<Option<ChannelHttpHeaders>, String> {
sql::get_channel_headers_by_id(id).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 @@ -138,7 +138,7 @@ fn extract_headers(l2: &mut String, lines: &mut Skip<Enumerate<Lines<BufReader<F
http_origin: None,
referrer: None,
user_agent: None,
ignore_ssl: false
ignore_ssl: None
};
let mut at_least_one: bool = false;
while l2.starts_with("#EXTVLCOPT") {
Expand Down
6 changes: 4 additions & 2 deletions src-tauri/src/mpv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,10 @@ fn set_headers(headers: Option<ChannelHttpHeaders>, args: &mut Vec<String>) {
if let Some(user_agent) = headers.user_agent {
args.push(format!("{ARG_USER_AGENT}{user_agent}"));
}
if headers.ignore_ssl {
args.push(ARG_IGNORE_SSL.to_string());
if let Some(ignore_ssl) = headers.ignore_ssl {
if ignore_ssl == true {
args.push(ARG_IGNORE_SSL.to_string());
}
}
let headers = headers_vec.join(",");
args.push(format!("{ARG_HTTP_HEADERS}{headers}"));
Expand Down
1 change: 1 addition & 0 deletions src-tauri/src/source_type.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub const M3U: u8 = 0;
pub const M3U_LINK: u8 = 1;
pub const XTREAM: u8 = 2;
pub const CUSTOM: u8 = 3;
160 changes: 145 additions & 15 deletions src-tauri/src/sql.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use std::{collections::HashMap, sync::LazyLock};

use crate::log::log;
use crate::types::CustomChannel;
use crate::{
media_type,
media_type, source_type,
types::{Channel, ChannelHttpHeaders, Filters, Source},
view_type,
};
Expand All @@ -12,6 +14,8 @@ use r2d2_sqlite::SqliteConnectionManager;
use rusqlite::{params, params_from_iter, OptionalExtension, Row, Transaction};

const PAGE_SIZE: u8 = 36;
pub const CUSTOM_CHANNELS_SOURCE_RESERVED_NAME: &str = "My custom channels";
const CUSTOM_CHANNELS_GROUP_RESERVED_NAME: &str = "Custom channels";
static CONN: LazyLock<Pool<SqliteConnectionManager>> = LazyLock::new(|| create_connection_pool());

pub fn get_conn() -> Result<PooledConnection<SqliteConnectionManager>> {
Expand Down Expand Up @@ -70,7 +74,7 @@ CREATE TABLE "channel_http_headers" (
"user_agent" varchar(500),
"http_origin" varchar(500),
"ignore_ssl" integer DEFAULT 0,
FOREIGN KEY (channel_id) REFERENCES channels(id)
FOREIGN KEY (channel_id) REFERENCES channels(id) ON DELETE CASCADE
);
CREATE TABLE "settings" (
Expand Down Expand Up @@ -103,7 +107,7 @@ CREATE INDEX index_channel_media_type ON channels(media_type);
CREATE INDEX index_group_source_id ON groups(source_id);
CREATE INDEX index_channel_http_headers_channel_id ON channel_http_headers(channel_id);
CREATE UNIQUE INDEX index_channel_http_headers_channel_id ON channel_http_headers(channel_id);
"#,
)?;
Ok(())
Expand Down Expand Up @@ -169,7 +173,7 @@ pub fn insert_channel(tx: &Transaction, channel: Channel) -> Result<()> {
tx.execute(
r#"
INSERT OR IGNORE INTO channels (name, group_id, image, url, source_id, media_type, series_id, favorite)
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, false);
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8);
"#,
params![
channel.name,
Expand All @@ -178,7 +182,8 @@ VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, false);
channel.url,
channel.source_id,
channel.media_type as u8,
channel.series_id
channel.series_id,
channel.favorite
],
)?;
Ok(())
Expand Down Expand Up @@ -449,16 +454,6 @@ fn row_to_channel(row: &Row) -> std::result::Result<Channel, rusqlite::Error> {

pub fn delete_channels_by_source(source_id: i64) -> Result<()> {
let sql = get_conn()?;
sql.execute(
r#"
DELETE
FROM channel_http_headers ch
JOIN channels c ON ch.channel_id = c.id
WHERE c.source_id = ?
AND c.favorite = 0;
"#,
params![source_id.to_string()],
)?;
sql.execute(
r#"
DELETE FROM channels
Expand Down Expand Up @@ -527,6 +522,9 @@ pub fn get_channel_count_by_source(id: i64) -> Result<u64> {
}

pub fn source_name_exists(name: String) -> Result<bool> {
if name.to_lowercase().trim() == CUSTOM_CHANNELS_SOURCE_RESERVED_NAME.to_lowercase() {
return Ok(true);
}
let sql = get_conn()?;
Ok(sql
.query_row(
Expand Down Expand Up @@ -613,6 +611,138 @@ pub fn set_source_enabled(value: bool, source_id: i64) -> Result<()> {
Ok(())
}

pub fn add_custom_channel(channel: CustomChannel) -> Result<()> {
let mut source = get_custom_source();
create_or_find_source_by_name(&mut source)?;
let mut sql = get_conn()?;
let tx = sql.transaction()?;
match add_custom_channel_tx(&tx, channel, source.id.unwrap()) {
Ok(_) => {
tx.commit()?;
Ok(())
},
Err(e) => {
tx.rollback().unwrap_or_else(|e| log(format!("{:?}", e)));
return Err(e.context("Failed to add custom channel"));
}
}
}

fn add_custom_channel_tx(
tx: &Transaction,
mut channel: CustomChannel,
source_id: i64,
) -> Result<()> {
channel.data.group_id = Some(get_or_create_group(
CUSTOM_CHANNELS_GROUP_RESERVED_NAME,
source_id,
tx,
)?);
channel.data.source_id = source_id;
insert_channel(tx, channel.data)?;
if let Some(mut headers) = channel.headers {
headers.channel_id = Some(tx.last_insert_rowid());
insert_channel_headers(tx, headers)?;
}
Ok(())
}

fn get_or_create_group(name: &str, source_id: i64, tx: &Transaction) -> Result<i64> {
let id = tx
.query_row(
"SELECT id FROM groups WHERE name = ?",
params![name],
|row| row.get("id"),
)
.optional()?;
if id.is_none() {
tx.execute(
"INSERT INTO groups (name, source_id) VALUES (?, ?)",
params![name, source_id],
)?;
return Ok(tx.last_insert_rowid());
}
Ok(id.context("No group id")?)
}

pub fn get_custom_source() -> Source {
Source {
id: None,
name: CUSTOM_CHANNELS_SOURCE_RESERVED_NAME.to_string(),
enabled: true,
username: None,
password: None,
source_type: source_type::CUSTOM,
url: None,
url_origin: None,
}
}

pub fn edit_custom_channel(channel: CustomChannel) -> Result<()> {
let mut sql = get_conn()?;
let tx = sql.transaction()?;
match edit_custom_channel_tx(channel, &tx) {
Ok(_) => {
tx.commit()?;
Ok(())
},
Err(e) => {
tx.rollback().unwrap_or_else(|e| log(format!("{:?}", e)));
return Err(e);
}
}
}

fn edit_custom_channel_tx(channel: CustomChannel, tx: &Transaction) -> Result<()> {
tx.execute(
r#"
UPDATE channels
SET name = ?, image = ?, url = ?, media_type = ?
WHERE id = ?
"#,
params![
channel.data.name,
channel.data.image,
channel.data.url,
channel.data.media_type,
channel.data.id
],
)?;
if let Some(mut headers) = channel.headers {
headers.channel_id = channel.data.id;
tx.execute(
r#"
INSERT INTO channel_http_headers (referrer, user_agent, http_origin, ignore_ssl, channel_id)
VALUES (?1, ?2, ?3, ?4, ?5)
ON CONFLICT(channel_id) DO UPDATE SET
referrer = ?1,
user_agent = ?2,
http_origin = ?3,
ignore_ssl = ?4
"#,
params![
headers.referrer,
headers.user_agent,
headers.http_origin,
headers.ignore_ssl,
headers.channel_id
],
)?;
} else {
tx.execute(
"DELETE FROM channel_http_headers WHERE channel_id = ?",
params![channel.data.id],
)?;
}
Ok(())
}

pub fn delete_custom_channel(id: i64) -> Result<()> {
let sql = get_conn()?;
sql.execute("DELETE FROM channels WHERE id = ?", params![id])?;
Ok(())
}

#[cfg(test)]
mod test_sql {
use std::collections::HashMap;
Expand Down
8 changes: 7 additions & 1 deletion src-tauri/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,11 @@ pub struct ChannelHttpHeaders {
pub referrer: Option<String>,
pub user_agent: Option<String>,
pub http_origin: Option<String>,
pub ignore_ssl: bool
pub ignore_ssl: Option<bool>
}

#[derive(Clone, PartialEq, Debug, Deserialize, Serialize)]
pub struct CustomChannel {
pub data: Channel,
pub headers: Option<ChannelHttpHeaders>
}
4 changes: 1 addition & 3 deletions src-tauri/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ use crate::{m3u, source_type, sql, types::Source, xtream};
use anyhow::{anyhow, Context, Result};

pub async fn refresh_source(source: Source) -> Result<()> {

sql::delete_channels_by_source(source.id.context("no source ID")?)?;
sql::delete_groups_by_source(source.id.context("no source ID")?)?;
match source.source_type {
source_type::M3U => m3u::read_m3u8(source)?,
source_type::M3U_LINK => m3u::get_m3u8_from_link(source).await?,
source_type::XTREAM => xtream::get_xtream(source).await?,
source_type::CUSTOM => {},
_ => return Err(anyhow!("invalid source_type")),
}
Ok(())
Expand All @@ -21,5 +21,3 @@ pub async fn refresh_all() -> Result<()> {
}
Ok(())
}


2 changes: 2 additions & 0 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { ChannelTileComponent } from './channel-tile/channel-tile.component';
import { SettingsComponent } from './settings/settings.component';
import { SourceTileComponent } from './settings/source-tile/source-tile.component';
import { ErrorModalComponent } from './error-modal/error-modal.component';
import { EditChannelModalComponent } from './edit-channel-modal/edit-channel-modal.component';

@NgModule({
declarations: [
Expand All @@ -33,6 +34,7 @@ import { ErrorModalComponent } from './error-modal/error-modal.component';
SettingsComponent,
SourceTileComponent,
ErrorModalComponent,
EditChannelModalComponent,
],
imports: [
BrowserModule,
Expand Down
2 changes: 2 additions & 0 deletions src/app/channel-tile/channel-tile.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,7 @@
<ng-container *ngIf="!alreadyExistsInFav">Favorite</ng-container>
</button>
<button [hidden]="isMovie()" mat-menu-item (click)="record()">Record</button>
<button [hidden]="!isCustomChannel()" mat-menu-item (click)="edit()">Edit</button>
<button [hidden]="!isCustomChannel()" mat-menu-item (click)="delete()">Delete</button>
</ng-template>
</mat-menu>
Loading

0 comments on commit cb012b9

Please sign in to comment.