mirror of
https://github.com/euzu/tuliprox.git
synced 2026-10-19 22:32:26 +02:00
+1
-1
@@ -21,5 +21,5 @@ yarn.lock
|
||||
*.log
|
||||
|
||||
docker/web
|
||||
docker/m3u-filter
|
||||
docker/tuliprox
|
||||
docker/resources
|
||||
+18
-18
@@ -1,13 +1,13 @@
|
||||
# Changelog
|
||||
# 2.2.6 (2025-04-xx)
|
||||
- !BREAKING_CHANGE! all docker images have now m3u-filter under `/app`
|
||||
- !BREAKING_CHANGE! all docker images have now tuliprox under `/app`
|
||||
- !BREAKING CHANGE! bandwidth `throttle_kbps` attribute for `reverse_proxy.stream` in `config.yml`
|
||||
is now `throttle` and supports units. Allowed units are `KB/s`,`MB/s`,`KiB/s`,`MiB/s`,`kbps`,`mbps`,`Mibps`.
|
||||
Default unit is `kbps`.
|
||||
- !BREAKING_CHANGE! `log` config `active_clients` renamed to `log_active_user`
|
||||
- !BREAKING_CHANGE! user has now the attribute `ui_enabled` to disable/enable web_ui for user.
|
||||
You need to migrate the user db if you have used `use_user_db:true`.
|
||||
Set it to `false` run old m3u-filter version, then update m3u-filter and set `use_user_db:true`and start.
|
||||
Set it to `false` run old tuliprox version, then update tuliprox and set `use_user_db:true`and start.
|
||||
- !BREAKING_CHANGE! `web_ui config` restructured and added `user_ui_enabled` attribute
|
||||
```yaml
|
||||
web_ui:
|
||||
@@ -16,7 +16,7 @@ web_ui:
|
||||
path:
|
||||
auth:
|
||||
enabled: true
|
||||
issuer: m3u_filter
|
||||
issuer: tuliprox
|
||||
secret: ef9ab256a8c0abe5de92c2e05ca92baa810472ab702ff1674e9248308ceeec92
|
||||
userfile: user.txt
|
||||
```
|
||||
@@ -70,7 +70,7 @@ url: 'https://localhost.com/epg.xml'
|
||||
```
|
||||
```yaml
|
||||
# multi local file epg
|
||||
url: ['file:///${env:M3U_FILTER_HOME}/epg.xml', 'file:///${env:M3U_FILTER_HOME}/epg2.xml']
|
||||
url: ['file:///${env:TULIPROX_HOME}/epg.xml', 'file:///${env:TULIPROX_HOME}/epg2.xml']
|
||||
```
|
||||
```yaml
|
||||
# multi url epg
|
||||
@@ -175,9 +175,9 @@ ipcheck:
|
||||
- New custom streams `user_connections_exhausted` and `provider_connections_exhausted`added.
|
||||
```yaml
|
||||
custom_stream_response:
|
||||
channel_unavailable: /home/m3u-filter/resources/channel_unavailable.ts
|
||||
user_connections_exhausted: /home/m3u-filter/resources/user_connections_exhausted.ts
|
||||
provider_connections_exhausted: /home/m3u-filter/resources/provider_connections_exhausted.ts
|
||||
channel_unavailable: /home/tuliprox/resources/channel_unavailable.ts
|
||||
user_connections_exhausted: /home/tuliprox/resources/user_connections_exhausted.ts
|
||||
provider_connections_exhausted: /home/tuliprox/resources/provider_connections_exhausted.ts
|
||||
```
|
||||
- input alias definition for same provider with same content but different credentials
|
||||
```yaml
|
||||
@@ -204,7 +204,7 @@ There are 2 batch input types `xtream_batch` and `m3u_batch`.
|
||||
- sources:
|
||||
- inputs:
|
||||
- type: xtream_batch
|
||||
url: 'file:///home/m3u-filter/config/my_provider_batch.csv'
|
||||
url: 'file:///home/tuliprox/config/my_provider_batch.csv'
|
||||
targets:
|
||||
- name: test
|
||||
```
|
||||
@@ -220,7 +220,7 @@ my_provider_2;user2;password2;http://my_provider_2.com:8080;1;0
|
||||
- sources:
|
||||
- inputs:
|
||||
- type: m3u_batch
|
||||
url: 'file:///home/m3u-filter/config/my_provider_batch.csv'
|
||||
url: 'file:///home/tuliprox/config/my_provider_batch.csv'
|
||||
targets:
|
||||
- name: test
|
||||
```
|
||||
@@ -318,14 +318,14 @@ server:
|
||||
host: 192.169.1.9
|
||||
port: '8901'
|
||||
timezone: Europe/Paris
|
||||
message: Welcome to m3u-filter
|
||||
message: Welcome to tuliprox
|
||||
- name: external
|
||||
protocol: https
|
||||
host: m3ufilter.mydomain.tv
|
||||
host: tuliprox.mydomain.tv
|
||||
port: '443'
|
||||
timezone: Europe/Paris
|
||||
message: Welcome to m3u-filter
|
||||
path: m3uflt
|
||||
message: Welcome to tuliprox
|
||||
path: tuliprox
|
||||
```
|
||||
- Added Active clients count (for reverse proxy mode users) which is now displayed in `/status` and can be logged with setting
|
||||
`active_clients: true` under `log`section in `config.yml`
|
||||
@@ -430,7 +430,7 @@ reverse_proxy:
|
||||
|
||||
# 2.0.10 (2024-12-03)
|
||||
- added Target Output Option `m3u_include_type_in_url`, default false. This adds `live`, `movie`, `series` to the url of the stream in reverse proxy mode.
|
||||
- added Target Output Option `m3u_mask_redirect_url`, default false. The urls are pointed to m3u-filter in redirect mode. In stream request a redirect response is send. Usefully if you want to track calls in redirect mode.
|
||||
- added Target Output Option `m3u_mask_redirect_url`, default false. The urls are pointed to tuliprox in redirect mode. In stream request a redirect response is send. Usefully if you want to track calls in redirect mode.
|
||||
- fixed xtream api redirect url problem.
|
||||
|
||||
# 2.0.9 (2024-12-01)
|
||||
@@ -510,8 +510,8 @@ reverse_proxy:
|
||||
- `secret` secret for jwt token
|
||||
- `userfile` optional userfile with generated userfile in format "username: password" per file, default name is user.txt in config path
|
||||
* Password generation argument --genpwd to generate passwords for userfile.
|
||||
* Added env var `M3U_FILTER_LOG` for log level
|
||||
* Log Level has now module support like `m3u_filter::util=error,m3u_filter::filter=debug,m3u_filter=debug`
|
||||
* Added env var `TULIPROX_LOG` for log level
|
||||
* Log Level has now module support like `tuliprox::util=error,tuliprox::filter=debug,tuliprox=debug`
|
||||
* Multiple Xtream Sources merging into one target is now supported
|
||||
|
||||
# v1.1.8(2024-03-06)
|
||||
@@ -602,7 +602,7 @@ server:
|
||||
https_port:
|
||||
rtmp_port:
|
||||
timezone: Europe/Paris
|
||||
message: Welcome to m3u-filter
|
||||
message: Welcome to tuliprox
|
||||
user:
|
||||
- target: pl1
|
||||
credentials:
|
||||
@@ -614,7 +614,7 @@ user:
|
||||
|
||||
# Changelog
|
||||
# v1.0.0(2023-04-27)
|
||||
* Added target argument for command line. `m3u-filter -t <target_name> -t <target_name>`. Target names should be provided in the config.
|
||||
* Added target argument for command line. `tuliprox -t <target_name> -t <target_name>`. Target names should be provided in the config.
|
||||
* Added filter to mapper definition.
|
||||
* Refactored filter parsing.
|
||||
* Fixed sort after mapping group names.
|
||||
|
||||
Generated
+1
-1
@@ -1576,7 +1576,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||
|
||||
[[package]]
|
||||
name = "m3u-filter"
|
||||
name = "tuliprox"
|
||||
version = "2.2.5"
|
||||
dependencies = [
|
||||
"axum",
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "m3u-filter"
|
||||
name = "tuliprox"
|
||||
version = "2.2.5"
|
||||
edition = "2021"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# <img src="https://github.com/user-attachments/assets/8ef9ea79-62ff-4298-978f-22326c5c3d02" height="24" /> **m3u-filter** - A Powerful Playlist Processing & Proxy Tool
|
||||
# <img src="https://github.com/user-attachments/assets/8ef9ea79-62ff-4298-978f-22326c5c3d02" height="24" /> **tuliprox** - A Powerful Playlist Processing & Proxy Tool
|
||||
|
||||
**m3u-filter** is a flexible and efficient tool built in Rust 🦀,
|
||||
**tuliprox** is a flexible and efficient tool built in Rust 🦀,
|
||||
designed for processing, filtering, and serving media playlists with minimal resource usage.
|
||||
|
||||
<p align="center">
|
||||
@@ -34,7 +34,7 @@ Define complex filters using expressive logic, e.g.:
|
||||
|
||||
## Command line Arguments
|
||||
```
|
||||
Usage: m3u-filter [OPTIONS]
|
||||
Usage: tuliprox [OPTIONS]
|
||||
|
||||
Options:
|
||||
-p, --config-path <CONFIG_PATH> The config directory
|
||||
@@ -56,7 +56,7 @@ Options:
|
||||
For running in cli mode, you need to define a `config.yml` file which can be inside config directory next to the executable or provided with the
|
||||
`-c` cli argument.
|
||||
|
||||
For running specific targets use the `-t` argument like `m3u-filter -t <target_name> -t <other_target_name>`.
|
||||
For running specific targets use the `-t` argument like `tuliprox -t <target_name> -t <other_target_name>`.
|
||||
Target names should be provided in the config. The -t option overrides `enabled` attributes of `input` and `target` elements.
|
||||
This means, even disabled inputs and targets are processed when the given target name as cli argument matches a target.
|
||||
|
||||
@@ -85,7 +85,7 @@ Default is `0`.
|
||||
If you process the same provider multiple times each thread uses a connection. Keep in mind that you hit the provider max-connection.
|
||||
|
||||
### 1.2. `api`
|
||||
`api` contains the `server-mode` settings. To run `m3u-filter` in `server-mode` you need to start it with the `-s`cli argument.
|
||||
`api` contains the `server-mode` settings. To run `tuliprox` in `server-mode` you need to start it with the `-s`cli argument.
|
||||
-`api: {host: localhost, port: 8901, web_root: ./web}`
|
||||
|
||||
### 1.3. `working_dir`
|
||||
@@ -238,7 +238,7 @@ LRU-Cache is for resources. If it is `enabled`, the resources/images are persist
|
||||
In an LRU cache, the least recently used items are evicted to make room for new items if the cache `size`is exceeded.
|
||||
|
||||
#### 1.6.3 `resource_rewrite_disabled`
|
||||
If you have m3u-filter behind a reverse proxy and dont want rewritten resource urls inside responses, you can disable the resource_url rewrite.
|
||||
If you have tuliprox behind a reverse proxy and dont want rewritten resource urls inside responses, you can disable the resource_url rewrite.
|
||||
Default value is false.
|
||||
If you set it `true` `cache` is disabled! Because the cache cant work without rewritten urls.
|
||||
|
||||
@@ -268,7 +268,7 @@ if set to true, an update is started when the application starts.
|
||||
- `sanitize_sensitive_info` default true
|
||||
- `active_clients` default false, if set to true reverse proxy client count is printed as info log.
|
||||
- `log_level` can be set to `trace`, `debug`, `info`, `warn` and `error`.
|
||||
You can also set module based level like `hyper_util::client::legacy::connect=error,m3u_filter=debug`
|
||||
You can also set module based level like `hyper_util::client::legacy::connect=error,tuliprox=debug`
|
||||
|
||||
|
||||
`log_level` priority CLI-Argument, Env-Var, Config, Default(`info`).
|
||||
@@ -291,13 +291,13 @@ web_ui_enabled: true
|
||||
web_auth:
|
||||
enabled: true
|
||||
secret: very.secret.secret
|
||||
issuer: m3u_filter
|
||||
issuer: tuliprox
|
||||
userfile: user.txt
|
||||
```
|
||||
|
||||
- `web_auth` can be deactivated if `enabled` is set to `false`. If not set default is `true`.
|
||||
- `secret` is used for jwt token generation.
|
||||
- `userfile` is the file where the ui users are stored. if the filename is not absolute `m3u-filter` will look into the `config_dir`. if `userfile`is not given the default value is `user.txt`
|
||||
- `userfile` is the file where the ui users are stored. if the filename is not absolute `tuliprox` will look into the `config_dir`. if `userfile`is not given the default value is `user.txt`
|
||||
|
||||
You can generate a secret for jwt token for example with `node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"`
|
||||
|
||||
@@ -310,12 +310,12 @@ nobody: $argon2id$v=$argon2id$v=19$m=19456,t=2,p=1$Y2FROE83ZDQ1c2VaYmJ4VU9YdHpuZ
|
||||
|
||||
The password can be generated with
|
||||
```shell
|
||||
./m3u-filter --genpwd`
|
||||
./tuliprox --genpwd`
|
||||
```
|
||||
|
||||
or with docker
|
||||
```shell
|
||||
docker container exec -it m3u-filter ./m3u-filter --genpwd
|
||||
docker container exec -it tuliprox ./tuliprox --genpwd
|
||||
```
|
||||
|
||||
The encrypted pasword needs to be added manually into the users file.
|
||||
@@ -364,9 +364,9 @@ and add it to the `config.yml`.
|
||||
|
||||
```yaml
|
||||
custom_stream_response:
|
||||
channel_unavailable: /home/m3u-filter/channel_unavailable.ts
|
||||
user_connections_exhausted: /home/m3u-filter/user_connections_exhausted.ts
|
||||
provider_connections_exhausted: /home/m3u-filter/provider_connections_exhausted.ts
|
||||
channel_unavailable: /home/tuliprox/channel_unavailable.ts
|
||||
user_connections_exhausted: /home/tuliprox/user_connections_exhausted.ts
|
||||
provider_connections_exhausted: /home/tuliprox/provider_connections_exhausted.ts
|
||||
```
|
||||
|
||||
### 1.15 `user_config_dir`
|
||||
@@ -419,7 +419,7 @@ HdHomeRun device config has the following attributes:
|
||||
- `firmware_version`: _optional_
|
||||
- `device_type`: _optional_
|
||||
- `device_udn`: _optional_
|
||||
- `port`: _optional_, if not given the m3u-filter-server port is incremented for each device.
|
||||
- `port`: _optional_, if not given the tuliprox-server port is incremented for each device.
|
||||
|
||||
|
||||
## 2. `source.yml`
|
||||
@@ -543,7 +543,7 @@ There are 2 batch input types `xtream_batch` and `m3u_batch`.
|
||||
- sources:
|
||||
- inputs:
|
||||
- type: xtream_batch
|
||||
url: 'file:///home/m3u-filter/config/my_provider_batch.csv'
|
||||
url: 'file:///home/tuliprox/config/my_provider_batch.csv'
|
||||
targets:
|
||||
- name: test
|
||||
```
|
||||
@@ -559,7 +559,7 @@ my_provider_2;user2;password2;http://my_provider_2.com:8080;1;0
|
||||
- sources:
|
||||
- inputs:
|
||||
- type: m3u_batch
|
||||
url: 'file:///home/m3u-filter/config/my_provider_batch.csv'
|
||||
url: 'file:///home/tuliprox/config/my_provider_batch.csv'
|
||||
targets:
|
||||
- name: test
|
||||
```
|
||||
@@ -1131,7 +1131,7 @@ mappings:
|
||||
|
||||
## 3. Api-Proxy Config
|
||||
|
||||
If you use m3u-filter to deliver playlists, we require a configuration to provide the necessary server information, rewrite URLs in reverse proxy mode, and define users who can access the API.
|
||||
If you use tuliprox to deliver playlists, we require a configuration to provide the necessary server information, rewrite URLs in reverse proxy mode, and define users who can access the API.
|
||||
|
||||
For this purpose, we use the `api-proxy.yml` configuration.
|
||||
|
||||
@@ -1147,14 +1147,14 @@ server:
|
||||
host: 192.169.1.9
|
||||
port: '8901'
|
||||
timezone: Europe/Paris
|
||||
message: Welcome to m3u-filter
|
||||
message: Welcome to tuliprox
|
||||
- name: external
|
||||
protocol: https
|
||||
host: m3ufilter.mydomain.tv
|
||||
host: tuliprox.mydomain.tv
|
||||
port: '443'
|
||||
timezone: Europe/Paris
|
||||
message: Welcome to m3u-filter
|
||||
path: m3uflt
|
||||
message: Welcome to tuliprox
|
||||
path: tuliprox
|
||||
```
|
||||
|
||||
User definitions are made for the targets. Each target can have multiple users. Usernames and tokens must be unique.
|
||||
@@ -1215,11 +1215,11 @@ If you use the endpoints through rest calls, you can use, for the sake of simpli
|
||||
When you define credentials for a `target`, ensure that this target has
|
||||
`output` format `xtream`or `m3u`.
|
||||
|
||||
The `proxy` property can be `reverse`or `redirect`. `reverse` means the streams are going through m3u-filter, `redirect` means the streams are comming from your provider.
|
||||
The `proxy` property can be `reverse`or `redirect`. `reverse` means the streams are going through tuliprox, `redirect` means the streams are comming from your provider.
|
||||
|
||||
If you use `https` you need a ssl terminator. `m3u-filter` does not support https traffic.
|
||||
If you use `https` you need a ssl terminator. `tuliprox` does not support https traffic.
|
||||
|
||||
If you use a ssl-terminator or proxy in front of m3u-filter you can set a `path` to make the configuration of your proxy simpler.
|
||||
If you use a ssl-terminator or proxy in front of tuliprox you can set a `path` to make the configuration of your proxy simpler.
|
||||
For example you use `nginx` as your reverse proxy.
|
||||
|
||||
`api-proxy.yml`
|
||||
@@ -1230,14 +1230,14 @@ server:
|
||||
host: 192.169.1.9
|
||||
port: '8901'
|
||||
timezone: Europe/Paris
|
||||
message: Welcome to m3u-filter
|
||||
message: Welcome to tuliprox
|
||||
- name: external
|
||||
protocol: https
|
||||
host: m3ufilter.mydomain.tv
|
||||
host: tuliprox.mydomain.tv
|
||||
port: '443'
|
||||
timezone: Europe/Paris
|
||||
message: Welcome to m3u-filter
|
||||
path: m3uflt
|
||||
message: Welcome to tuliprox
|
||||
path: tuliprox
|
||||
user:
|
||||
- target: xc_m3u
|
||||
credentials:
|
||||
@@ -1253,8 +1253,8 @@ user:
|
||||
|
||||
Now you can do `nginx` configuration like
|
||||
```config
|
||||
location /m3uflt {
|
||||
rewrite ^/m3uflt/(.*)$ /$1 break;
|
||||
location /tuliprox {
|
||||
rewrite ^/tuliprox/(.*)$ /$1 break;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-NginX-Proxy true;
|
||||
@@ -1273,14 +1273,14 @@ server:
|
||||
host: 192.168.0.3
|
||||
port: 80
|
||||
timezone: Europe/Paris
|
||||
message: Welcome to m3u-filter
|
||||
message: Welcome to tuliprox
|
||||
- name: external
|
||||
protocol: https
|
||||
host: my_external_domain.com
|
||||
port: 443
|
||||
timezone: Europe/Paris
|
||||
message: Welcome to m3u-filter
|
||||
path: /m3uflt
|
||||
message: Welcome to tuliprox
|
||||
path: /tuliprox
|
||||
- target: pl1
|
||||
credentials:
|
||||
- {username: x3452, password: ztrhgrGZ, token: 4342sd, proxy: reverse, server: external, epg_timeshift: -2:30}
|
||||
@@ -1297,16 +1297,16 @@ Following log levels are supported:
|
||||
|
||||
Use the `-l` or `--log-level` cli-argument to specify the log-level.
|
||||
|
||||
The log level can be set through environment variable `M3U_FILTER_LOG`,
|
||||
The log level can be set through environment variable `TULIPROX_LOG`,
|
||||
or config.
|
||||
|
||||
Precedence is cli-argument, env-var, config, default(`info`).
|
||||
|
||||
Log Level has module support like `m3u_filter::util=error,m3u_filter::filter=debug,m3u_filter=debug`
|
||||
Log Level has module support like `tuliprox::util=error,tuliprox::filter=debug,tuliprox=debug`
|
||||
|
||||
## 6. Web-UI
|
||||
|
||||
The WebUI is for configuration the m3u-filter config.
|
||||
The WebUI is for configuration the tuliprox config.
|
||||
|
||||
If you enable authentication, users can log in with their accounts (you can disable login per user),
|
||||
and configure their playlist.
|
||||
@@ -1317,13 +1317,13 @@ and configure their playlist.
|
||||
Change into the root directory and run:
|
||||
|
||||
```shell
|
||||
docker build --rm -f docker/Dockerfile -t m3u-filter .
|
||||
docker build --rm -f docker/Dockerfile -t tuliprox .
|
||||
```
|
||||
|
||||
This will build the complete project and create a docker image.
|
||||
|
||||
To start the container, you can use the `docker-compose.yml`
|
||||
But you need to change `image: ghcr.io/euzu/m3u-filter:latest` to `image: m3u-filter`
|
||||
But you need to change `image: ghcr.io/euzu/tuliprox:latest` to `image: tuliprox`
|
||||
|
||||
|
||||
### Manual build static binary for docker
|
||||
@@ -1358,41 +1358,41 @@ Targets are
|
||||
|
||||
```shell
|
||||
# Build for a specific architecture
|
||||
docker build --rm -f docker/Dockerfile -t m3u-filter --target scratch-final --build-arg RUST_TARGET=x86_64-unknown-linux-musl .
|
||||
docker build --rm -f docker/Dockerfile -t tuliprox --target scratch-final --build-arg RUST_TARGET=x86_64-unknown-linux-musl .
|
||||
|
||||
docker build --rm -f docker/Dockerfile -t m3u-filter --target scratch-final --build-arg RUST_TARGET=aarch64-unknown-linux-musl .
|
||||
docker build --rm -f docker/Dockerfile -t tuliprox --target scratch-final --build-arg RUST_TARGET=aarch64-unknown-linux-musl .
|
||||
|
||||
docker build --rm -f docker/Dockerfile -t m3u-filter --target scratch-final --build-arg RUST_TARGET=armv7-unknown-linux-musleabihf .
|
||||
docker build --rm -f docker/Dockerfile -t tuliprox --target scratch-final --build-arg RUST_TARGET=armv7-unknown-linux-musleabihf .
|
||||
|
||||
docker build --rm -f docker/Dockerfile -t m3u-filter --target scratch-final --build-arg RUST_TARGET=x86_64-apple-darwin .
|
||||
docker build --rm -f docker/Dockerfile -t tuliprox --target scratch-final --build-arg RUST_TARGET=x86_64-apple-darwin .
|
||||
```
|
||||
##### docker-compose.yml
|
||||
```docker
|
||||
version: '3'
|
||||
services:
|
||||
m3u-filter:
|
||||
container_name: m3u-filter
|
||||
image: m3u-filter
|
||||
tuliprox:
|
||||
container_name: tuliprox
|
||||
image: tuliprox
|
||||
user: "133:144"
|
||||
working_dir: /app
|
||||
volumes:
|
||||
- /opt/m3u-filter/config:/app/config
|
||||
- /opt/m3u-filter/data:/app/data
|
||||
- /opt/m3u-filter/backup:/app/backup
|
||||
- /opt/m3u-filter/downloads:/app/downloads
|
||||
- /opt/tuliprox/config:/app/config
|
||||
- /opt/tuliprox/data:/app/data
|
||||
- /opt/tuliprox/backup:/app/backup
|
||||
- /opt/tuliprox/downloads:/app/downloads
|
||||
environment:
|
||||
- TZ=Europe/Paris
|
||||
ports:
|
||||
- "8901:8901"
|
||||
restart: unless-stopped
|
||||
```
|
||||
This example is for the local image, the official can be found under `ghcr.io/euzu/m3u-filter:latest`
|
||||
This example is for the local image, the official can be found under `ghcr.io/euzu/tuliprox:latest`
|
||||
|
||||
If you want to use m3u-filter with docker-compose, there is a `--healthcheck` argument for healthchecks
|
||||
If you want to use tuliprox with docker-compose, there is a `--healthcheck` argument for healthchecks
|
||||
|
||||
```docker
|
||||
healthcheck:
|
||||
test: ["CMD", "/app/m3u-filter", "-p", "/app/config" "--healthcheck"]
|
||||
test: ["CMD", "/app/tuliprox", "-p", "/app/config" "--healthcheck"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
@@ -1406,29 +1406,29 @@ To get it started in a Alpine 3.19 LXC
|
||||
apk update
|
||||
apk add nano git yarn bash cargo perl-local-lib perl-module-build make
|
||||
cd /opt
|
||||
git clone https://github.com/euzu/m3u-filter.git
|
||||
cd /opt/m3u-filter/bin
|
||||
git clone https://github.com/euzu/tuliprox.git
|
||||
cd /opt/tuliprox/bin
|
||||
./build_lin.sh
|
||||
ln -s /opt/m3u-filter/target/release/m3u-filter /bin/m3u-filter
|
||||
cd /opt/m3u-filter/frontend
|
||||
ln -s /opt/tuliprox/target/release/tuliprox /bin/tuliprox
|
||||
cd /opt/tuliprox/frontend
|
||||
yarn
|
||||
yarn build
|
||||
ln -s /opt/m3u-filter/frontend/build /web
|
||||
ln -s /opt/m3u-filter/config /config
|
||||
ln -s /opt/tuliprox/frontend/build /web
|
||||
ln -s /opt/tuliprox/config /config
|
||||
mkdir /data
|
||||
mkdir /backup
|
||||
```
|
||||
|
||||
**Creating a service, create /etc/init.d/m3u-filter**
|
||||
**Creating a service, create /etc/init.d/tuliprox**
|
||||
```shell
|
||||
#!/sbin/openrc-run
|
||||
name=m3u-filter
|
||||
command="/bin/m3u-filter"
|
||||
name=tuliprox
|
||||
command="/bin/tuliprox"
|
||||
command_args="-p /config -s"
|
||||
command_user="root"
|
||||
command_background="yes"
|
||||
output_log="/var/log/m3u-filter/m3u-filter.log"
|
||||
error_log="/var/log/m3u-filter/m3u-filter.log"
|
||||
output_log="/var/log/tuliprox/tuliprox.log"
|
||||
error_log="/var/log/tuliprox/tuliprox.log"
|
||||
supervisor="supervise-daemon"
|
||||
|
||||
depend() {
|
||||
@@ -1437,13 +1437,13 @@ depend() {
|
||||
|
||||
start_pre() {
|
||||
checkpath --directory --owner $command_user:$command_user --mode 0775 \
|
||||
/run/m3u-filter /var/log/m3u-filter
|
||||
/run/tuliprox /var/log/tuliprox
|
||||
}
|
||||
```
|
||||
|
||||
**then add it to boot**
|
||||
```shell
|
||||
rc-update add m3u-filter default
|
||||
rc-update add tuliprox default
|
||||
```
|
||||
|
||||
|
||||
@@ -1476,10 +1476,10 @@ env RUSTFLAGS="--remap-path-prefix $HOME=~" cross build --release --target armv
|
||||
```
|
||||
|
||||
# Different Scenarios
|
||||
## Using `m3u-filter` with a m3u provider.
|
||||
## Using `tuliprox` with a m3u provider.
|
||||
todo.
|
||||
|
||||
## Using `m3u-filter` with a xtream provider.
|
||||
## Using `tuliprox` with a xtream provider.
|
||||
|
||||
You have a provider who supports the xtream api.
|
||||
|
||||
@@ -1490,7 +1490,7 @@ The provider gives you:
|
||||
- epg_url: `http://fantastic.provider.xyz:8080/xmltv.php?username=tvjunkie&password=junkie.secret`
|
||||
|
||||
|
||||
To use `m3u-filter` you need to create the configuration.
|
||||
To use `tuliprox` you need to create the configuration.
|
||||
The configuration consist of 4 files.
|
||||
- config.yml
|
||||
- source.yml
|
||||
@@ -1507,7 +1507,7 @@ working_dir: ./data
|
||||
update_on_boot: true
|
||||
```
|
||||
|
||||
This configuration starts `m3u-filter`and listens on the 8901 port. The downloaded playlists are stored inside the `data`-folder in the current working directory.
|
||||
This configuration starts `tuliprox`and listens on the 8901 port. The downloaded playlists are stored inside the `data`-folder in the current working directory.
|
||||
The property `update_on_boot` is optional and can be helpful in the beginning until you have found a working configuration. I prefer to set it to false.
|
||||
|
||||
Now we have to define the sources we want to import. We do this inside `source.yml`
|
||||
@@ -1549,13 +1549,13 @@ server:
|
||||
host: 192.168.1.41
|
||||
port: '8901'
|
||||
timezone: Europe/Berlin
|
||||
message: Welcome to m3u-filter
|
||||
message: Welcome to tuliprox
|
||||
- name: external
|
||||
protocol: https
|
||||
host: tvjunkie.dyndns.org
|
||||
port: '443'
|
||||
timezone: Europe/Berlin
|
||||
message: Welcome to m3u-filter
|
||||
message: Welcome to tuliprox
|
||||
user:
|
||||
- target: all_channels
|
||||
credentials:
|
||||
@@ -1568,11 +1568,11 @@ user:
|
||||
proxy: redirect
|
||||
server: external
|
||||
```
|
||||
We have defined 2 server configurations. The `default` configuration is intended for use in the local network, the IP address is that of the computer on which `m3u-filter` is running. The `external` configuration is optional and is only required for access from outside your local network. External access requires port forwarding on your router and an SSL terminator proxy such as nginx and a dyndns provider configured from your router if you do not have a static IP address (this is outside the scope of this manual).
|
||||
We have defined 2 server configurations. The `default` configuration is intended for use in the local network, the IP address is that of the computer on which `tuliprox` is running. The `external` configuration is optional and is only required for access from outside your local network. External access requires port forwarding on your router and an SSL terminator proxy such as nginx and a dyndns provider configured from your router if you do not have a static IP address (this is outside the scope of this manual).
|
||||
|
||||
The next section of the `api-proxy.yml` contains the user definition. We can define users for each `target` from the `source.yml`.
|
||||
This means that each `user` can only access one `target` from `source.yml`. We have named our target `all_channels` in `source.yml` and used this name for the user definition. We have defined 2 users, one for local access and one for external access.
|
||||
We have set the proxy type to `redirect`, which means that the client will be redirected to the original provider URL when opening a stream. If you set the proxy type to `reverse`, the stream will be streamed from the provider through `m3u-filter`. Based on the hardware you are running `m3u-filter` on, you can opt for the proxy type `reverse`. But you should start with `redirect` first until everything works well.
|
||||
We have set the proxy type to `redirect`, which means that the client will be redirected to the original provider URL when opening a stream. If you set the proxy type to `reverse`, the stream will be streamed from the provider through `tuliprox`. Based on the hardware you are running `tuliprox` on, you can opt for the proxy type `reverse`. But you should start with `redirect` first until everything works well.
|
||||
|
||||
If no server is specified for a user, the default one is taken.
|
||||
|
||||
@@ -1583,7 +1583,7 @@ All this information are now defined in `api-proxy.yml`.
|
||||
- username: `xt`
|
||||
- password: `xt.secret`
|
||||
|
||||
Start `m3u-filter`, fire up your IPTV-Application, enter credentials and watch.
|
||||
Start `tuliprox`, fire up your IPTV-Application, enter credentials and watch.
|
||||
|
||||
# It works well, but I don't need all the channels, how can I filter?
|
||||
|
||||
@@ -1614,7 +1614,7 @@ Ok now let's start.
|
||||
|
||||
First: We have a lot of channel groups we dont need.
|
||||
|
||||
`m3u-filter` excludes or includes groups or channels based on filter. Usable fields for filter are `Group`, `Name` and `Title`.
|
||||
`tuliprox` excludes or includes groups or channels based on filter. Usable fields for filter are `Group`, `Name` and `Title`.
|
||||
The simplest filter is:
|
||||
|
||||
`<Field> ~ <Regular Expression>`. For example `Group ~ ".*"`. This means include all categories.
|
||||
|
||||
@@ -13,8 +13,8 @@ declare -A ARCHITECTURES=(
|
||||
)
|
||||
|
||||
declare -A BUILDS=(
|
||||
[LINUX]="m3u-filter:scratch-final m3u-filter-alpine:alpine-final"
|
||||
[AARCH64]="m3u-filter-aarch64:scratch-final"
|
||||
[LINUX]="tuliprox:scratch-final tuliprox-alpine:alpine-final"
|
||||
[AARCH64]="tuliprox-aarch64:scratch-final"
|
||||
)
|
||||
|
||||
cd "$FRONTEND_DIR" && rm -rf build && yarn && yarn build
|
||||
@@ -47,7 +47,7 @@ for PLATFORM in "${!ARCHITECTURES[@]}"; do
|
||||
cargo clean || true
|
||||
env RUSTFLAGS="--remap-path-prefix $HOME=~" cross build --release --target "$ARCHITECTURE"
|
||||
|
||||
BINARY_PATH="${WORKING_DIR}/target/${ARCHITECTURE}/release/m3u-filter"
|
||||
BINARY_PATH="${WORKING_DIR}/target/${ARCHITECTURE}/release/tuliprox"
|
||||
if [ ! -f "$BINARY_PATH" ]; then
|
||||
echo "🧨 Error: Binary $BINARY_PATH does not exist."
|
||||
exit 1
|
||||
@@ -89,7 +89,7 @@ done
|
||||
# Clean up
|
||||
echo "🗑 Cleaning up Docker context..."
|
||||
rm -rf "${DOCKER_DIR}/web"
|
||||
rm -f "${DOCKER_DIR}/m3u-filter"
|
||||
rm -f "${DOCKER_DIR}/tuliprox"
|
||||
rm -rf "${DOCKER_DIR}/resources"
|
||||
|
||||
echo "🎉 Docker images for version ${VERSION} have been successfully built, tagged, and pushed."
|
||||
|
||||
@@ -61,13 +61,13 @@ cargo clean
|
||||
env RUSTFLAGS="--remap-path-prefix $HOME=~" cross build --release --target "$TARGET"
|
||||
|
||||
# Check if the binary exists
|
||||
if [ ! -f "${WORKING_DIR}/target/${TARGET}/release/m3u-filter" ]; then
|
||||
echo "Error: Static binary '${WORKING_DIR}/target/${TARGET}/release/m3u-filter' does not exist."
|
||||
if [ ! -f "${WORKING_DIR}/target/${TARGET}/release/tuliprox" ]; then
|
||||
echo "Error: Static binary '${WORKING_DIR}/target/${TARGET}/release/tuliprox' does not exist."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Prepare Docker build context
|
||||
cp "${WORKING_DIR}/target/${TARGET}/release/m3u-filter" "${DOCKER_DIR}/"
|
||||
cp "${WORKING_DIR}/target/${TARGET}/release/tuliprox" "${DOCKER_DIR}/"
|
||||
rm -rf "${DOCKER_DIR}/web"
|
||||
cp -r "${WORKING_DIR}/frontend/build" "${DOCKER_DIR}/web"
|
||||
cp -r "${RESOURCES_DIR}" "${DOCKER_DIR}/"
|
||||
@@ -75,9 +75,9 @@ cp -r "${RESOURCES_DIR}" "${DOCKER_DIR}/"
|
||||
cd "${DOCKER_DIR}"
|
||||
echo "Building Docker images for version ${VERSION}"
|
||||
if [ "$beta_image" = true ]; then
|
||||
SCRATCH_IMAGE_NAME=m3u-filter-aarch64-beta
|
||||
SCRATCH_IMAGE_NAME=tuliprox-aarch64-beta
|
||||
else
|
||||
SCRATCH_IMAGE_NAME=m3u-filter-aarch64
|
||||
SCRATCH_IMAGE_NAME=tuliprox-aarch64
|
||||
fi
|
||||
|
||||
# Build scratch image and tag as "latest"
|
||||
@@ -94,7 +94,7 @@ docker push ghcr.io/euzu/${SCRATCH_IMAGE_NAME}:latest
|
||||
# Clean up
|
||||
echo "Cleaning up build artifacts..."
|
||||
rm -rf "${DOCKER_DIR}/web"
|
||||
rm -f "${DOCKER_DIR}/m3u-filter"
|
||||
rm -f "${DOCKER_DIR}/tuliprox"
|
||||
rm -rf "${DOCKER_DIR}/resources"
|
||||
|
||||
echo "Docker images ghcr.io/euzu/${SCRATCH_IMAGE_NAME}:${VERSION} have been successfully built, tagged, and pushed."
|
||||
|
||||
@@ -38,20 +38,20 @@ cargo clean
|
||||
env RUSTFLAGS="--remap-path-prefix $HOME=~" cross build --release --target "$TARGET"
|
||||
|
||||
# Check if the binary exists
|
||||
if [ ! -f "${WORKING_DIR}/target/${TARGET}/release/m3u-filter" ]; then
|
||||
echo "Error: Static binary '${WORKING_DIR}/target/${TARGET}/release/m3u-filter' does not exist."
|
||||
if [ ! -f "${WORKING_DIR}/target/${TARGET}/release/tuliprox" ]; then
|
||||
echo "Error: Static binary '${WORKING_DIR}/target/${TARGET}/release/tuliprox' does not exist."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Prepare Docker build context
|
||||
cp "${WORKING_DIR}/target/${TARGET}/release/m3u-filter" "${DOCKER_DIR}/"
|
||||
cp "${WORKING_DIR}/target/${TARGET}/release/tuliprox" "${DOCKER_DIR}/"
|
||||
rm -rf "${DOCKER_DIR}/web"
|
||||
cp -r "${WORKING_DIR}/frontend/build" "${DOCKER_DIR}/web"
|
||||
cp -r "${RESOURCES_DIR}" "${DOCKER_DIR}/"
|
||||
|
||||
cd "${DOCKER_DIR}"
|
||||
echo "Building Docker images for version ${VERSION}"
|
||||
BETA_IMAGE_NAME=m3u-filter-beta
|
||||
BETA_IMAGE_NAME=tuliprox-beta
|
||||
|
||||
# Build alpine image and tag as "latest"
|
||||
docker build -f Dockerfile-manual -t ghcr.io/euzu/${BETA_IMAGE_NAME}:"${VERSION}" --target alpine-final .
|
||||
@@ -67,7 +67,7 @@ docker push ghcr.io/euzu/${BETA_IMAGE_NAME}:latest
|
||||
# Clean up
|
||||
echo "Cleaning up build artifacts..."
|
||||
rm -rf "${DOCKER_DIR}/web"
|
||||
rm -f "${DOCKER_DIR}/m3u-filter"
|
||||
rm -f "${DOCKER_DIR}/tuliprox"
|
||||
rm -rf "${DOCKER_DIR}/resources"
|
||||
|
||||
echo "Docker images ghcr.io/euzu/${BETA_IMAGE_NAME}${VERSION} have been successfully built, tagged, and pushed."
|
||||
|
||||
+7
-7
@@ -39,11 +39,11 @@ declare -A ARCHITECTURES=(
|
||||
)
|
||||
|
||||
declare -A DIRS=(
|
||||
[LINUX]=m3u-filter_${VERSION}_linux_x86_64
|
||||
[WINDOWS]=m3u-filter_${VERSION}_windows_x86_64
|
||||
[ARM7]=m3u-filter_${VERSION}_armv7
|
||||
[AARCH64]=m3u-filter_${VERSION}_aarch64_x86_64
|
||||
[DARWIN]=m3u-filter_${VERSION}_apple-darwin_x86_64
|
||||
[LINUX]=tuliprox_${VERSION}_linux_x86_64
|
||||
[WINDOWS]=tuliprox_${VERSION}_windows_x86_64
|
||||
[ARM7]=tuliprox_${VERSION}_armv7
|
||||
[AARCH64]=tuliprox_${VERSION}_aarch64_x86_64
|
||||
[DARWIN]=tuliprox_${VERSION}_apple-darwin_x86_64
|
||||
)
|
||||
|
||||
# Special case mapping for binary extensions (e.g., Windows needs .exe)
|
||||
@@ -73,9 +73,9 @@ for PLATFORM in "${!ARCHITECTURES[@]}"; do
|
||||
ARC=${DIR}.tgz
|
||||
# Handle platform-specific binary file names
|
||||
if [[ -n "${BIN_EXTENSIONS[$PLATFORM]}" ]]; then
|
||||
BIN="${ARCHITECTURE}/release/m3u-filter${BIN_EXTENSIONS[$PLATFORM]}"
|
||||
BIN="${ARCHITECTURE}/release/tuliprox${BIN_EXTENSIONS[$PLATFORM]}"
|
||||
else
|
||||
BIN="${ARCHITECTURE}/release/m3u-filter"
|
||||
BIN="${ARCHITECTURE}/release/tuliprox"
|
||||
fi
|
||||
|
||||
rustup target add "$ARCHITECTURE"
|
||||
|
||||
@@ -4,15 +4,15 @@ server:
|
||||
host: 192.168.9.3
|
||||
port: 80
|
||||
timezone: Europe/Paris
|
||||
message: Welcome to m3u-filter
|
||||
message: Welcome to tuliprox
|
||||
- name: extern
|
||||
protocol: https
|
||||
host: m3u-filter.mydns.com
|
||||
host: tuliprox.mydns.com
|
||||
port: 443
|
||||
rtmp_port:
|
||||
timezone: Europe/Paris
|
||||
message: Welcome to m3u-filter
|
||||
path: m3uflt # optional, only needed for easier reverse proxy configuration see README.md.
|
||||
message: Welcome to tuliprox
|
||||
path: tuliprox # optional, only needed for easier reverse proxy configuration see README.md.
|
||||
user:
|
||||
- target: pl1
|
||||
credentials:
|
||||
|
||||
+8
-8
@@ -1,12 +1,12 @@
|
||||
api: { host: 127.0.0.1, port: 8901, web_root: /home/m3u-filter/web }
|
||||
api: { host: 127.0.0.1, port: 8901, web_root: /home/tuliprox/web }
|
||||
threads: 0
|
||||
working_dir: /home/m3u-filter/data
|
||||
backup_dir: /home/m3u-filter/.backup
|
||||
working_dir: /home/tuliprox/data
|
||||
backup_dir: /home/tuliprox/.backup
|
||||
update_on_boot: false
|
||||
custom_stream_response:
|
||||
channel_unavailable: /home/m3u-filter/resources/channel_unavailable.ts
|
||||
user_connections_exhausted: /home/m3u-filter/resources/user_connections_exhausted.ts
|
||||
provider_connections_exhausted: /home/m3u-filter/resources/provider_connections_exhausted.ts
|
||||
channel_unavailable: /home/tuliprox/resources/channel_unavailable.ts
|
||||
user_connections_exhausted: /home/tuliprox/resources/user_connections_exhausted.ts
|
||||
provider_connections_exhausted: /home/tuliprox/resources/provider_connections_exhausted.ts
|
||||
|
||||
connect_timeout_secs: 10
|
||||
|
||||
@@ -22,7 +22,7 @@ reverse_proxy:
|
||||
cache:
|
||||
size: 500MB
|
||||
enabled: true
|
||||
dir: /home/m3u-filter/cache
|
||||
dir: /home/tuliprox/cache
|
||||
log:
|
||||
sanitize_sensitive_info: false
|
||||
active_clients: true
|
||||
@@ -33,7 +33,7 @@ web_ui:
|
||||
path:
|
||||
auth:
|
||||
enabled: true
|
||||
issuer: m3u_filter
|
||||
issuer: tuliprox
|
||||
secret: ef9ab256a8c0abe5de92c2e05ca92baa810472ab702ff1674e9248308ceeec92
|
||||
userfile: user.txt
|
||||
|
||||
|
||||
+4
-4
@@ -70,10 +70,10 @@ ARG RUST_TARGET
|
||||
WORKDIR /app
|
||||
COPY --from=rust-build /usr/share/zoneinfo /usr/share/zoneinfo
|
||||
COPY --from=rust-build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
COPY --from=rust-build /src/target/${RUST_TARGET}/release/m3u-filter ./m3u-filter
|
||||
COPY --from=rust-build /src/target/${RUST_TARGET}/release/tuliprox ./tuliprox
|
||||
COPY --from=node-build /app/build ./web
|
||||
COPY --from=resource-build /src/resources ./resources
|
||||
ENTRYPOINT ["/app/m3u-filter"]
|
||||
ENTRYPOINT ["/app/tuliprox"]
|
||||
CMD ["-s", "-p", "/app/config"]
|
||||
|
||||
# Final container (Alpine)
|
||||
@@ -84,8 +84,8 @@ ARG RUST_TARGET
|
||||
RUN apk add --no-cache bash curl ca-certificates tini
|
||||
|
||||
WORKDIR /app
|
||||
COPY --from=rust-build /src/target/${RUST_TARGET}/release/m3u-filter ./m3u-filter
|
||||
COPY --from=rust-build /src/target/${RUST_TARGET}/release/tuliprox ./tuliprox
|
||||
COPY --from=node-build /app/build ./web
|
||||
COPY --from=resource-build /src/resources ./resources
|
||||
ENTRYPOINT ["/sbin/tini", "--", "/app/m3u-filter"]
|
||||
ENTRYPOINT ["/sbin/tini", "--", "/app/tuliprox"]
|
||||
CMD ["-s", "-p", "/app/config"]
|
||||
|
||||
@@ -7,11 +7,11 @@ WORKDIR /app
|
||||
COPY --from=build /usr/share/zoneinfo /usr/share/zoneinfo
|
||||
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
|
||||
COPY ./m3u-filter ./
|
||||
COPY ./tuliprox ./
|
||||
COPY ./web ./web
|
||||
COPY ./resources ./resources
|
||||
|
||||
CMD ["/app/m3u-filter", "-s", "-p", "/app/config"]
|
||||
CMD ["/app/tuliprox", "-s", "-p", "/app/config"]
|
||||
|
||||
# Alpine Final container
|
||||
FROM alpine:latest AS alpine-final
|
||||
@@ -20,11 +20,11 @@ RUN apk add --no-cache bash curl strace tcpdump bind-tools nano ca-certificates
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY ./m3u-filter ./
|
||||
COPY ./tuliprox ./
|
||||
COPY ./web ./web
|
||||
COPY ./resources ./resources
|
||||
# config should be mounted as volume
|
||||
# COPY ./config ./config
|
||||
|
||||
ENTRYPOINT ["/sbin/tini", "--", "/app/m3u-filter"]
|
||||
ENTRYPOINT ["/sbin/tini", "--", "/app/tuliprox"]
|
||||
CMD ["-s", "-p", "/app/config"]
|
||||
+8
-8
@@ -8,26 +8,26 @@ Change into the root directory and run:
|
||||
|
||||
```shell
|
||||
# Build for a specific architecture
|
||||
docker build --rm -f docker/Dockerfile -t m3u-filter --target scratch-final --build-arg RUST_TARGET=x86_64-unknown-linux-musl .
|
||||
docker build --rm -f docker/Dockerfile -t tuliprox --target scratch-final --build-arg RUST_TARGET=x86_64-unknown-linux-musl .
|
||||
|
||||
docker build --rm -f docker/Dockerfile -t m3u-filter --target scratch-final --build-arg RUST_TARGET=aarch64-unknown-linux-musl .
|
||||
docker build --rm -f docker/Dockerfile -t tuliprox --target scratch-final --build-arg RUST_TARGET=aarch64-unknown-linux-musl .
|
||||
|
||||
docker build --rm -f docker/Dockerfile -t m3u-filter --target scratch-final --build-arg RUST_TARGET=armv7-unknown-linux-musleabihf .
|
||||
docker build --rm -f docker/Dockerfile -t tuliprox --target scratch-final --build-arg RUST_TARGET=armv7-unknown-linux-musleabihf .
|
||||
|
||||
docker build --rm -f docker/Dockerfile -t m3u-filter --target scratch-final --build-arg RUST_TARGET=x86_64-apple-darwin .
|
||||
docker build --rm -f docker/Dockerfile -t tuliprox --target scratch-final --build-arg RUST_TARGET=x86_64-apple-darwin .
|
||||
```
|
||||
Both targets have the path prefix: `/app`
|
||||
|
||||
This will build the complete project and create a docker image.
|
||||
|
||||
To start the container, you can use the `docker-compose.yml`
|
||||
But you need to change `image: ghcr.io/euzu/m3u-filter:latest` to `image: m3u-filter`
|
||||
But you need to change `image: ghcr.io/euzu/tuliprox:latest` to `image: tuliprox`
|
||||
|
||||
# Manual docker image
|
||||
|
||||
You want to build the binary and web folder manually and create a docker image.
|
||||
|
||||
To dockerize m3u-filter, you need to compile a static build.
|
||||
To dockerize tuliprox, you need to compile a static build.
|
||||
The static build can created with `bin\build_lin_static.sh`.
|
||||
Description of static binary compiling is in the main `README.md`
|
||||
|
||||
@@ -36,7 +36,7 @@ Then you need to compile the frontend with `yarn build`
|
||||
Change into the `docker` directory and copy all the needed files (look at the Dockerfile) into the current directory.
|
||||
|
||||
To create a docker image type:
|
||||
`docker -f Dockerfile-manual build -t m3u-filter .`
|
||||
`docker -f Dockerfile-manual build -t tuliprox .`
|
||||
|
||||
To start the container, you can use the `docker-compose.yml`
|
||||
But you need to change `image: ghcr.io/euzu/m3u-filter:latest` to `image: m3u-filter`
|
||||
But you need to change `image: ghcr.io/euzu/tuliprox:latest` to `image: tuliprox`
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
version: '3'
|
||||
services:
|
||||
m3u-filter:
|
||||
container_name: m3u-filter
|
||||
image: ghcr.io/euzu/m3u-filter:latest
|
||||
tuliprox:
|
||||
container_name: tuliprox
|
||||
image: ghcr.io/euzu/tuliprox:latest
|
||||
working_dir: /app
|
||||
volumes:
|
||||
- ./data:/app/data
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "m3u-filter",
|
||||
"description": "m3u-filter",
|
||||
"name": "tuliprox",
|
||||
"description": "tuliprox",
|
||||
"author": "euzu",
|
||||
"version": "2.2.5",
|
||||
"private": true,
|
||||
|
||||
@@ -186,7 +186,7 @@
|
||||
"THROTTLE": "Supported units are KB/s,MB/s,KiB/s,MiB/s,kbps,mbps,Mibps"
|
||||
},
|
||||
"WEB_UI": {
|
||||
"PATH": "Configuration of a path prefix, allowing it to be accessed behind a reverse proxy under a custom subpath. like https://yourdomain.com/m3uflt"
|
||||
"PATH": "Configuration of a path prefix, allowing it to be accessed behind a reverse proxy under a custom subpath. like https://yourdomain.com/tuliprox"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -197,7 +197,7 @@
|
||||
"FINISH_SETUP": "Finish Setup"
|
||||
},
|
||||
"MSG": {
|
||||
"WELCOME": "Welcome to m3u-filter setup. This wizard will guide you through the initial setup process."
|
||||
"WELCOME": "Welcome to tuliprox setup. This wizard will guide you through the initial setup process."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"app_title": "m3u-filter",
|
||||
"app_title": "tuliprox",
|
||||
"app_logo": null,
|
||||
"api": {
|
||||
"apiUrl": "/api/v1/",
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<!-- link rel="manifest" href="%PUBLIC_URL%/assets/manifest.json" / -->
|
||||
<title>m3u-filter</title>
|
||||
<title>tuliprox</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"short_name": "m3u-filter",
|
||||
"name": "m3u-filter",
|
||||
"short_name": "tuliprox",
|
||||
"name": "tuliprox",
|
||||
"icons": [
|
||||
{
|
||||
"src": "assets/favicon.ico",
|
||||
|
||||
@@ -13,7 +13,7 @@ interface AppUserProps {
|
||||
|
||||
export default function AppUser(props: AppUserProps) {
|
||||
const services = useServices();
|
||||
const appTitle = useMemo(() => services.config().getUiConfig().app_title ?? 'm3u-filter', [services]);
|
||||
const appTitle = useMemo(() => services.config().getUiConfig().app_title ?? 'tuliprox', [services]);
|
||||
const appLogo = useMemo(() => {
|
||||
let logo = services.config().getUiConfig().app_logo;
|
||||
if (logo) {
|
||||
|
||||
@@ -22,7 +22,7 @@ export default function App(props: AppProps) {
|
||||
const [serverConfig, setServerConfig] = useState<ServerConfig>(undefined);
|
||||
const serverConfigLoaded = useRef(false);
|
||||
const [preferencesVisible, setPreferencesVisible] = useState<boolean>(true);
|
||||
const appTitle = useMemo(() => services.config().getUiConfig().app_title ?? 'm3u-filter', [services]);
|
||||
const appTitle = useMemo(() => services.config().getUiConfig().app_title ?? 'tuliprox', [services]);
|
||||
const translate = useTranslator();
|
||||
const appLogo = useMemo(() => {
|
||||
let logo = services.config().getUiConfig().app_logo;
|
||||
|
||||
@@ -11,7 +11,7 @@ export default function Login(): JSX.Element {
|
||||
const usernameRef = useRef<HTMLInputElement>(undefined);
|
||||
const passwordRef = useRef<HTMLInputElement>(undefined);
|
||||
const services = useServices();
|
||||
const appTitle = useMemo(() => services.config().getUiConfig().app_title ?? 'm3u-filter', [services]);
|
||||
const appTitle = useMemo(() => services.config().getUiConfig().app_title ?? 'tuliprox', [services]);
|
||||
const appLogo = useMemo(() => {
|
||||
let logo = services.config().getUiConfig().app_logo;
|
||||
if (logo) {
|
||||
|
||||
@@ -10,7 +10,7 @@ export interface UiConfig {
|
||||
|
||||
export const DefaultUiConfig: UiConfig = {
|
||||
languages: ["en", "de", "fr", "tr", "es", "it", "ru"],
|
||||
app_title: "m3u-filter",
|
||||
app_title: "tuliprox",
|
||||
api: {
|
||||
apiUrl: "/api/v1/",
|
||||
authUrl: "/auth/"
|
||||
|
||||
@@ -518,7 +518,7 @@ fn is_throttled_stream(item_type: PlaylistItemType, throttle_kbps: usize) -> boo
|
||||
throttle_kbps > 0 && matches!(item_type, PlaylistItemType::Video | PlaylistItemType::Series | PlaylistItemType::SeriesInfo)
|
||||
}
|
||||
|
||||
const SESSION_COOKIE_NAME: &str = "m3uflt_session=";
|
||||
const SESSION_COOKIE_NAME: &str = "tuliprox_session=";
|
||||
|
||||
// fn create_delete_session_cookie() -> String {
|
||||
// format!("{SESSION_COOKIE_NAME}; Max-Age=0; Path=/; HttpOnly")
|
||||
|
||||
@@ -12,7 +12,7 @@ use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
use std::{fs};
|
||||
use axum::response::IntoResponse;
|
||||
use crate::m3u_filter_error::to_io_error;
|
||||
use crate::tuliprox_error::to_io_error;
|
||||
use crate::utils::request::create_client;
|
||||
|
||||
async fn download_file(active: Arc<RwLock<Option<FileDownload>>>, client: &reqwest::Client) -> Result<(), String> {
|
||||
|
||||
@@ -166,7 +166,7 @@ async fn create_device(app_state: &Arc<HdHomerunAppState>) -> Option<Device> {
|
||||
Some(Device {
|
||||
friendly_name: device.friendly_name.to_string(),
|
||||
manufacturer: device.manufacturer.to_string(),
|
||||
//manufacturer_url: "https://github.com/euzu/m3u-filter".to_string(),
|
||||
//manufacturer_url: "https://github.com/euzu/tuliprox".to_string(),
|
||||
model_number: device.model_number.to_string(),
|
||||
model_name: device.model_name.to_string(),
|
||||
firmware_name: device.firmware_name.to_string(),
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::api::model::config::{ServerConfig, ServerInputConfig, ServerSourceCon
|
||||
use crate::api::model::request::{PlaylistRequest, PlaylistRequestType};
|
||||
use crate::auth::access_token::create_access_token;
|
||||
use crate::auth::authenticator::validator_admin;
|
||||
use crate::m3u_filter_error::M3uFilterError;
|
||||
use crate::tuliprox_error::TuliProxError;
|
||||
use crate::model::StatusCheck;
|
||||
use crate::model::XtreamPlaylistItem;
|
||||
use crate::model::{validate_targets, Config, ConfigDto, ConfigInput, ConfigInputOptions, ConfigSource, ConfigTarget, InputType};
|
||||
@@ -23,7 +23,7 @@ use serde_json::json;
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
use std::sync::Arc;
|
||||
|
||||
fn intern_save_config_api_proxy(backup_dir: &str, api_proxy: &ApiProxyConfig, file_path: &str) -> Option<M3uFilterError> {
|
||||
fn intern_save_config_api_proxy(backup_dir: &str, api_proxy: &ApiProxyConfig, file_path: &str) -> Option<TuliProxError> {
|
||||
match config_reader::save_api_proxy(file_path, backup_dir, api_proxy) {
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
@@ -34,7 +34,7 @@ fn intern_save_config_api_proxy(backup_dir: &str, api_proxy: &ApiProxyConfig, fi
|
||||
None
|
||||
}
|
||||
|
||||
fn intern_save_config_main(file_path: &str, backup_dir: &str, cfg: &ConfigDto) -> Option<M3uFilterError> {
|
||||
fn intern_save_config_main(file_path: &str, backup_dir: &str, cfg: &ConfigDto) -> Option<TuliProxError> {
|
||||
match config_reader::save_main_config(file_path, backup_dir, cfg) {
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
|
||||
@@ -10,9 +10,9 @@ use crate::api::model::request::UserApiRequest;
|
||||
use crate::api::model::streams::provider_stream::{create_custom_video_stream_response, CustomVideoStreamType}
|
||||
;
|
||||
use crate::api::model::xtream::XtreamAuthorizationResponse;
|
||||
use crate::m3u_filter_error::create_m3u_filter_error_result;
|
||||
use crate::m3u_filter_error::info_err;
|
||||
use crate::m3u_filter_error::{str_to_io_error, M3uFilterError, M3uFilterErrorKind};
|
||||
use crate::tuliprox_error::create_tuliprox_error_result;
|
||||
use crate::tuliprox_error::info_err;
|
||||
use crate::tuliprox_error::{str_to_io_error, TuliProxError, TuliProxErrorKind};
|
||||
use crate::model::{ProxyType, ProxyUserCredentials, UserConnectionPermission};
|
||||
use crate::model::TargetType;
|
||||
use crate::model::{Config, ConfigInput, ConfigTarget};
|
||||
@@ -80,15 +80,15 @@ impl TryFrom<XtreamCluster> for XtreamApiStreamContext {
|
||||
}
|
||||
|
||||
impl FromStr for XtreamApiStreamContext {
|
||||
type Err = M3uFilterError;
|
||||
type Err = TuliProxError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, M3uFilterError> {
|
||||
fn from_str(s: &str) -> Result<Self, TuliProxError> {
|
||||
match s.to_lowercase().as_str() {
|
||||
Self::LIVE => Ok(Self::Live),
|
||||
Self::MOVIE => Ok(Self::Movie),
|
||||
Self::SERIES => Ok(Self::Series),
|
||||
Self::TIMESHIFT => Ok(Self::Timeshift),
|
||||
_ => create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "Unknown CounterModifier: {}", s)
|
||||
_ => create_tuliprox_error_result!(TuliProxErrorKind::Info, "Unknown CounterModifier: {}", s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::api::api_utils::get_username_from_auth_header;
|
||||
use crate::model::WebAuthConfig;
|
||||
use crate::api::model::app_state::AppState;
|
||||
use crate::auth::auth_bearer::AuthBearer;
|
||||
use crate::m3u_filter_error::to_io_error;
|
||||
use crate::tuliprox_error::to_io_error;
|
||||
|
||||
const ROLE_ADMIN: &str = "ADMIN";
|
||||
const ROLE_USER: &str = "USER";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use rand::{Rng};
|
||||
use rand::distr::Alphanumeric;
|
||||
use crate::m3u_filter_error::str_to_io_error;
|
||||
use crate::tuliprox_error::str_to_io_error;
|
||||
|
||||
fn generate_salt(length: usize) -> String {
|
||||
let salt: String = rand::rng()
|
||||
|
||||
+22
-22
@@ -6,11 +6,11 @@ use log::{debug, error, log_enabled, trace, Level};
|
||||
use pest::iterators::Pair;
|
||||
use pest::Parser;
|
||||
|
||||
use crate::m3u_filter_error::{M3uFilterError, M3uFilterErrorKind};
|
||||
use crate::tuliprox_error::{TuliProxError, TuliProxErrorKind};
|
||||
use crate::model::ItemField;
|
||||
use crate::model::{PlaylistItem, PlaylistItemType};
|
||||
use crate::tools::directed_graph::DirectedGraph;
|
||||
use crate::m3u_filter_error::{create_m3u_filter_error_result, info_err};
|
||||
use crate::tuliprox_error::{create_tuliprox_error_result, info_err};
|
||||
use crate::utils::CONSTANTS;
|
||||
use crate::utils::exit;
|
||||
|
||||
@@ -234,7 +234,7 @@ impl std::fmt::Display for Filter {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_parser_item_field(expr: &Pair<Rule>) -> Result<ItemField, M3uFilterError> {
|
||||
fn get_parser_item_field(expr: &Pair<Rule>) -> Result<ItemField, TuliProxError> {
|
||||
if expr.as_rule() == Rule::field {
|
||||
let field_text = expr.as_str();
|
||||
for item in all::<ItemField>() {
|
||||
@@ -243,13 +243,13 @@ fn get_parser_item_field(expr: &Pair<Rule>) -> Result<ItemField, M3uFilterError>
|
||||
}
|
||||
}
|
||||
}
|
||||
create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "unknown field: {}", expr.as_str())
|
||||
create_tuliprox_error_result!(TuliProxErrorKind::Info, "unknown field: {}", expr.as_str())
|
||||
}
|
||||
|
||||
fn get_parser_regexp(
|
||||
expr: &Pair<Rule>,
|
||||
templates: &Vec<PatternTemplate>,
|
||||
) -> Result<RegexWithCaptures, M3uFilterError> {
|
||||
) -> Result<RegexWithCaptures, TuliProxError> {
|
||||
if expr.as_rule() == Rule::regexp {
|
||||
let mut parsed_text = String::from(expr.as_str());
|
||||
parsed_text.pop();
|
||||
@@ -257,7 +257,7 @@ fn get_parser_regexp(
|
||||
let regstr = apply_templates_to_pattern(&parsed_text, templates);
|
||||
let re = regex::Regex::new(regstr.as_str());
|
||||
if re.is_err() {
|
||||
return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "cant parse regex: {}", regstr);
|
||||
return create_tuliprox_error_result!(TuliProxErrorKind::Info, "cant parse regex: {}", regstr);
|
||||
}
|
||||
let regexp = re.unwrap();
|
||||
let captures = regexp
|
||||
@@ -275,13 +275,13 @@ fn get_parser_regexp(
|
||||
captures,
|
||||
});
|
||||
}
|
||||
create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "unknown field: {}", expr.as_str())
|
||||
create_tuliprox_error_result!(TuliProxErrorKind::Info, "unknown field: {}", expr.as_str())
|
||||
}
|
||||
|
||||
fn get_parser_field_comparison(
|
||||
expr: Pair<Rule>,
|
||||
templates: &Vec<PatternTemplate>,
|
||||
) -> Result<Filter, M3uFilterError> {
|
||||
) -> Result<Filter, TuliProxError> {
|
||||
let mut expr_inner = expr.into_inner();
|
||||
match get_parser_item_field(&expr_inner.next().unwrap()) {
|
||||
Ok(field) => match get_parser_regexp(&expr_inner.next().unwrap(), templates) {
|
||||
@@ -311,11 +311,11 @@ fn get_filter_item_type(text_item_type: &str) -> Option<PlaylistItemType> {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_parser_type_comparison(expr: Pair<Rule>) -> Result<Filter, M3uFilterError> {
|
||||
fn get_parser_type_comparison(expr: Pair<Rule>) -> Result<Filter, TuliProxError> {
|
||||
let expr_inner = expr.into_inner();
|
||||
let text_item_type = expr_inner.as_str();
|
||||
let item_type = get_filter_item_type(text_item_type);
|
||||
item_type.map_or_else(|| create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "cant parse item type: {text_item_type}"), |itype| Ok(Filter::TypeComparison(ItemField::Type, itype)))
|
||||
item_type.map_or_else(|| create_tuliprox_error_result!(TuliProxErrorKind::Info, "cant parse item type: {text_item_type}"), |itype| Ok(Filter::TypeComparison(ItemField::Type, itype)))
|
||||
}
|
||||
|
||||
macro_rules! handle_expr {
|
||||
@@ -396,12 +396,12 @@ fn get_parser_expression(
|
||||
stmts.pop().unwrap()
|
||||
}
|
||||
|
||||
fn get_parser_binary_op(expr: &Pair<Rule>) -> Result<BinaryOperator, M3uFilterError> {
|
||||
fn get_parser_binary_op(expr: &Pair<Rule>) -> Result<BinaryOperator, TuliProxError> {
|
||||
match expr.as_rule() {
|
||||
Rule::and => Ok(BinaryOperator::And),
|
||||
Rule::or => Ok(BinaryOperator::Or),
|
||||
_ => create_m3u_filter_error_result!(
|
||||
M3uFilterErrorKind::Info,
|
||||
_ => create_tuliprox_error_result!(
|
||||
TuliProxErrorKind::Info,
|
||||
"Unknown binary operator {}",
|
||||
expr.as_str()
|
||||
),
|
||||
@@ -411,7 +411,7 @@ fn get_parser_binary_op(expr: &Pair<Rule>) -> Result<BinaryOperator, M3uFilterEr
|
||||
pub fn get_filter(
|
||||
filter_text: &str,
|
||||
templates: Option<&Vec<PatternTemplate>>,
|
||||
) -> Result<Filter, M3uFilterError> {
|
||||
) -> Result<Filter, TuliProxError> {
|
||||
let empty_list = Vec::with_capacity(0);
|
||||
let template_list: &Vec<PatternTemplate> = templates.unwrap_or(&empty_list);
|
||||
let source = apply_templates_to_pattern(filter_text, template_list);
|
||||
@@ -471,8 +471,8 @@ pub fn get_filter(
|
||||
|
||||
result.map_or_else(
|
||||
|| {
|
||||
create_m3u_filter_error_result!(
|
||||
M3uFilterErrorKind::Info,
|
||||
create_tuliprox_error_result!(
|
||||
TuliProxErrorKind::Info,
|
||||
"Unable to parse filter: {}",
|
||||
&filter_text
|
||||
)
|
||||
@@ -480,13 +480,13 @@ pub fn get_filter(
|
||||
Ok,
|
||||
)
|
||||
}
|
||||
Err(err) => create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "{}", err),
|
||||
Err(err) => create_tuliprox_error_result!(TuliProxErrorKind::Info, "{}", err),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_dependency_graph(
|
||||
templates: &Vec<PatternTemplate>,
|
||||
) -> Result<DirectedGraph<String>, M3uFilterError> {
|
||||
) -> Result<DirectedGraph<String>, TuliProxError> {
|
||||
let mut graph = DirectedGraph::<String>::new();
|
||||
for template in templates {
|
||||
graph.add_node(&template.name);
|
||||
@@ -508,8 +508,8 @@ fn build_dependency_graph(
|
||||
);
|
||||
}
|
||||
if !cycles.is_empty() {
|
||||
return create_m3u_filter_error_result!(
|
||||
M3uFilterErrorKind::Info,
|
||||
return create_tuliprox_error_result!(
|
||||
TuliProxErrorKind::Info,
|
||||
"Cyclic dependencies in templates detected!"
|
||||
);
|
||||
}
|
||||
@@ -518,7 +518,7 @@ fn build_dependency_graph(
|
||||
|
||||
pub fn prepare_templates(
|
||||
templates: &Vec<PatternTemplate>,
|
||||
) -> Result<Vec<PatternTemplate>, M3uFilterError> {
|
||||
) -> Result<Vec<PatternTemplate>, TuliProxError> {
|
||||
let graph = build_dependency_graph(templates)?;
|
||||
let mut template_values = HashMap::<String, String>::new();
|
||||
let mut template_map: HashMap<String, PatternTemplate> = templates
|
||||
@@ -535,7 +535,7 @@ pub fn prepare_templates(
|
||||
if let Some(depends_on) = dependencies.get(&template_name) {
|
||||
let mut templ_value = template_values.get(&template_name).unwrap().to_string();
|
||||
for dep_templ_name in depends_on {
|
||||
let dep_value = template_values.get(dep_templ_name).ok_or_else(|| M3uFilterError::new(M3uFilterErrorKind::Info, format!("Failed to load template {dep_templ_name}")))?;
|
||||
let dep_value = template_values.get(dep_templ_name).ok_or_else(|| TuliProxError::new(TuliProxErrorKind::Info, format!("Failed to load template {dep_templ_name}")))?;
|
||||
templ_value =
|
||||
templ_value.replace(format!("!{dep_templ_name}!").as_str(), dep_value);
|
||||
}
|
||||
|
||||
+23
-23
@@ -9,7 +9,7 @@ macro_rules! get_errors_notify_message {
|
||||
} else {
|
||||
let text = $errors
|
||||
.iter()
|
||||
.filter(|&err| err.kind == M3uFilterErrorKind::Notify)
|
||||
.filter(|&err| err.kind == TuliProxErrorKind::Notify)
|
||||
.map(|err| err.message.as_str())
|
||||
.collect::<Vec<&str>>()
|
||||
.join("\r\n");
|
||||
@@ -27,7 +27,7 @@ pub use get_errors_notify_message;
|
||||
#[macro_export]
|
||||
macro_rules! notify_err {
|
||||
($text:expr) => {
|
||||
M3uFilterError::new(M3uFilterErrorKind::Notify, $text)
|
||||
TuliProxError::new(TuliProxErrorKind::Notify, $text)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -36,30 +36,30 @@ pub use notify_err;
|
||||
#[macro_export]
|
||||
macro_rules! info_err {
|
||||
($text:expr) => {
|
||||
M3uFilterError::new(M3uFilterErrorKind::Info, $text)
|
||||
TuliProxError::new(TuliProxErrorKind::Info, $text)
|
||||
};
|
||||
}
|
||||
pub use info_err;
|
||||
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! create_m3u_filter_error {
|
||||
macro_rules! create_tuliprox_error {
|
||||
($kind: expr, $($arg:tt)*) => {
|
||||
M3uFilterError::new($kind, format!($($arg)*))
|
||||
TuliProxError::new($kind, format!($($arg)*))
|
||||
}
|
||||
}
|
||||
pub use create_m3u_filter_error;
|
||||
pub use create_tuliprox_error;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! create_m3u_filter_error_result {
|
||||
macro_rules! create_tuliprox_error_result {
|
||||
($kind: expr, $($arg:tt)*) => {
|
||||
Err(M3uFilterError::new($kind, format!($($arg)*)))
|
||||
Err(TuliProxError::new($kind, format!($($arg)*)))
|
||||
}
|
||||
}
|
||||
pub use create_m3u_filter_error_result;
|
||||
pub use create_tuliprox_error_result;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! handle_m3u_filter_error_result_list {
|
||||
macro_rules! handle_tuliprox_error_result_list {
|
||||
($kind:expr, $result: expr) => {
|
||||
let errors = $result
|
||||
.filter_map(|result| {
|
||||
@@ -71,51 +71,51 @@ macro_rules! handle_m3u_filter_error_result_list {
|
||||
})
|
||||
.collect::<Vec<String>>();
|
||||
if !&errors.is_empty() {
|
||||
return Err(M3uFilterError::new($kind, errors.join("\n")));
|
||||
return Err(TuliProxError::new($kind, errors.join("\n")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub use handle_m3u_filter_error_result_list;
|
||||
pub use handle_tuliprox_error_result_list;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! handle_m3u_filter_error_result {
|
||||
macro_rules! handle_tuliprox_error_result {
|
||||
($kind:expr, $result: expr) => {
|
||||
if let Err(err) = $result {
|
||||
return Err(M3uFilterError::new($kind, err.to_string()));
|
||||
return Err(TuliProxError::new($kind, err.to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub use handle_m3u_filter_error_result;
|
||||
pub use handle_tuliprox_error_result;
|
||||
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum M3uFilterErrorKind {
|
||||
pub enum TuliProxErrorKind {
|
||||
// do not send with messaging
|
||||
Info,
|
||||
Notify, // send with messaging
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct M3uFilterError {
|
||||
pub kind: M3uFilterErrorKind,
|
||||
pub struct TuliProxError {
|
||||
pub kind: TuliProxErrorKind,
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
impl M3uFilterError {
|
||||
pub const fn new(kind: M3uFilterErrorKind, message: String) -> Self {
|
||||
impl TuliProxError {
|
||||
pub const fn new(kind: TuliProxErrorKind, message: String) -> Self {
|
||||
Self { kind, message }
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for M3uFilterError {
|
||||
impl Display for TuliProxError {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
write!(f, "M3uFilter error: {}", self.message)
|
||||
write!(f, "TuliProx error: {}", self.message)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for M3uFilterError {}
|
||||
impl Error for TuliProxError {}
|
||||
|
||||
pub fn to_io_error<E>(err: E) -> std::io::Error
|
||||
where
|
||||
|
||||
+2
-2
@@ -32,7 +32,7 @@ const LOG_ERROR_LEVEL_MOD: &[&str] = &[
|
||||
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(name = "m3u-filter")]
|
||||
#[command(name = "tuliprox")]
|
||||
#[command(author = "euzu <euzu@proton.me>")]
|
||||
#[command(version)]
|
||||
#[command(about = "Extended M3U playlist filter", long_about = None)]
|
||||
@@ -103,7 +103,7 @@ fn main() {
|
||||
let config_path: String = args.config_path.unwrap_or_else(file_utils::get_default_config_path);
|
||||
let config_file: String = args.config_file.unwrap_or_else(|| file_utils::get_default_config_file_path(&config_path));
|
||||
|
||||
let env_log_level = std::env::var("M3U_FILTER_LOG");
|
||||
let env_log_level = std::env::var("TULIPROX_LOG");
|
||||
init_logger(args.log_level.as_ref(), env_log_level.ok(), config_file.as_str());
|
||||
|
||||
if args.healthcheck {
|
||||
|
||||
+13
-13
@@ -1,5 +1,5 @@
|
||||
use crate::api::model::app_state::AppState;
|
||||
use crate::m3u_filter_error::{create_m3u_filter_error_result, info_err, M3uFilterError, M3uFilterErrorKind};
|
||||
use crate::tuliprox_error::{create_tuliprox_error_result, info_err, TuliProxError, TuliProxErrorKind};
|
||||
use crate::model::{Config, ClusterFlags};
|
||||
use crate::repository::user_repository::{backup_api_user_db_file, get_api_user_db_path, load_api_user, merge_api_user};
|
||||
use crate::utils::default_as_true;
|
||||
@@ -75,9 +75,9 @@ impl Display for ProxyType {
|
||||
}
|
||||
|
||||
impl FromStr for ProxyType {
|
||||
type Err = M3uFilterError;
|
||||
type Err = TuliProxError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, M3uFilterError> {
|
||||
fn from_str(s: &str) -> Result<Self, TuliProxError> {
|
||||
if s == Self::REDIRECT {
|
||||
return Ok(Self::Redirect);
|
||||
}
|
||||
@@ -95,7 +95,7 @@ impl FromStr for ProxyType {
|
||||
}
|
||||
}
|
||||
|
||||
create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "Unknown ProxyType: {}", s)
|
||||
create_tuliprox_error_result!(TuliProxErrorKind::Info, "Unknown ProxyType: {}", s)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,16 +170,16 @@ impl Display for ProxyUserStatus {
|
||||
}
|
||||
|
||||
impl FromStr for ProxyUserStatus {
|
||||
type Err = M3uFilterError;
|
||||
type Err = TuliProxError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, M3uFilterError> {
|
||||
fn from_str(s: &str) -> Result<Self, TuliProxError> {
|
||||
match s {
|
||||
Self::EXPIRED => Ok(Self::Expired),
|
||||
Self::BANNED => Ok(Self::Banned),
|
||||
Self::TRIAL => Ok(Self::Trial),
|
||||
Self::DISABLED => Ok(Self::Disabled),
|
||||
Self::PENDING => Ok(Self::Pending),
|
||||
_ => create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "Unknown ProxyType: {}", s)
|
||||
_ => create_tuliprox_error_result!(TuliProxErrorKind::Info, "Unknown ProxyType: {}", s)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -238,12 +238,12 @@ impl ProxyUserCredentials {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn validate(&self) -> Result<(), M3uFilterError> {
|
||||
pub fn validate(&self) -> Result<(), TuliProxError> {
|
||||
if self.username.is_empty() {
|
||||
return Err(M3uFilterError::new(M3uFilterErrorKind::Info, "Username required".to_string()));
|
||||
return Err(TuliProxError::new(TuliProxErrorKind::Info, "Username required".to_string()));
|
||||
}
|
||||
if self.password.is_empty() {
|
||||
return Err(M3uFilterError::new(M3uFilterErrorKind::Info, "Password required".to_string()));
|
||||
return Err(TuliProxError::new(TuliProxErrorKind::Info, "Password required".to_string()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -323,7 +323,7 @@ pub struct ApiProxyServerInfo {
|
||||
|
||||
impl ApiProxyServerInfo {
|
||||
|
||||
pub fn prepare(&mut self ) -> Result<(), M3uFilterError> {
|
||||
pub fn prepare(&mut self ) -> Result<(), TuliProxError> {
|
||||
self.name = self.name.trim().to_string();
|
||||
if self.name.is_empty() {
|
||||
return Err(info_err!("Server info name is empty ".to_string()));
|
||||
@@ -352,7 +352,7 @@ impl ApiProxyServerInfo {
|
||||
self.timezone = "UTC".to_string();
|
||||
}
|
||||
if self.message.is_empty() {
|
||||
self.message = "Welcome to m3u-filter".to_string();
|
||||
self.message = "Welcome to tuliprox".to_string();
|
||||
}
|
||||
if let Some(path) = &self.path {
|
||||
if path.trim().is_empty() {
|
||||
@@ -505,7 +505,7 @@ impl ApiProxyConfig {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prepare(&mut self, cfg: &Config) -> Result<(), M3uFilterError> {
|
||||
pub fn prepare(&mut self, cfg: &Config) -> Result<(), TuliProxError> {
|
||||
let mut errors = Vec::new();
|
||||
if self.server.is_empty() {
|
||||
errors.push("No server info defined".to_string());
|
||||
|
||||
+33
-33
@@ -11,14 +11,14 @@ use path_clean::PathClean;
|
||||
use rand::Rng;
|
||||
|
||||
use crate::foundation::filter::{prepare_templates, PatternTemplate};
|
||||
use crate::m3u_filter_error::{M3uFilterError, M3uFilterErrorKind};
|
||||
use crate::tuliprox_error::{TuliProxError, TuliProxErrorKind};
|
||||
use crate::model::{ApiProxyConfig, ApiProxyServerInfo, ProxyUserCredentials, Mappings};
|
||||
use crate::utils::{default_as_default, default_connect_timeout_secs, default_grace_period_millis, default_grace_period_timeout_secs};
|
||||
use crate::utils::file_lock_manager::FileLockManager;
|
||||
use crate::utils::file_utils;
|
||||
use crate::utils::{parse_size_base_2, parse_to_kbps};
|
||||
use crate::utils::exit;
|
||||
use crate::m3u_filter_error::{ create_m3u_filter_error_result};
|
||||
use crate::tuliprox_error::{ create_tuliprox_error_result};
|
||||
use crate::model::{ConfigInput, ConfigInputOptions, IpCheckConfig, ProxyConfig, ConfigSource, ConfigTarget, HdHomeRunConfig, LogConfig, MessagingConfig, ProcessTargets, TargetOutput, VideoConfig, WebUiConfig};
|
||||
|
||||
const STREAM_QUEUE_SIZE: usize = 1024; // mpsc channel holding messages. with 8192byte chunks and 2Mbit/s approx 8MB
|
||||
@@ -244,12 +244,12 @@ pub struct StreamConfig {
|
||||
}
|
||||
|
||||
impl StreamConfig {
|
||||
fn prepare(&mut self) -> Result<(), M3uFilterError> {
|
||||
fn prepare(&mut self) -> Result<(), TuliProxError> {
|
||||
if let Some(buffer) = self.buffer.as_mut() {
|
||||
buffer.prepare();
|
||||
}
|
||||
if let Some(throttle) = &self.throttle {
|
||||
self.throttle_kbps = parse_to_kbps(throttle).map_err(|err| M3uFilterError::new(M3uFilterErrorKind::Info, err))?;
|
||||
self.throttle_kbps = parse_to_kbps(throttle).map_err(|err| TuliProxError::new(TuliProxErrorKind::Info, err))?;
|
||||
}
|
||||
|
||||
if self.grace_period_millis > 0 {
|
||||
@@ -257,7 +257,7 @@ impl StreamConfig {
|
||||
let triple_ms = self.grace_period_millis * 3;
|
||||
self.grace_period_timeout_secs = std::cmp::max(1, triple_ms.div_ceil(1000));
|
||||
} else if self.grace_period_millis / 1000 > self.grace_period_timeout_secs {
|
||||
return Err(M3uFilterError::new(M3uFilterErrorKind::Info, format!("Grace time period timeout {} sec should be more than grace time period {} ms", self.grace_period_timeout_secs, self.grace_period_millis)));
|
||||
return Err(TuliProxError::new(TuliProxErrorKind::Info, format!("Grace time period timeout {} sec should be more than grace time period {} ms", self.grace_period_timeout_secs, self.grace_period_millis)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,12 +274,12 @@ pub struct RateLimitConfig {
|
||||
}
|
||||
|
||||
impl RateLimitConfig {
|
||||
fn prepare(&self) -> Result<(), M3uFilterError> {
|
||||
fn prepare(&self) -> Result<(), TuliProxError> {
|
||||
if self.period_millis == 0 {
|
||||
return Err(M3uFilterError::new(M3uFilterErrorKind::Info, "Rate limiter period can't be 0".to_string()));
|
||||
return Err(TuliProxError::new(TuliProxErrorKind::Info, "Rate limiter period can't be 0".to_string()));
|
||||
}
|
||||
if self.burst_size == 0 {
|
||||
return Err(M3uFilterError::new(M3uFilterErrorKind::Info, "Rate limiter bust can't be 0".to_string()));
|
||||
return Err(TuliProxError::new(TuliProxErrorKind::Info, "Rate limiter bust can't be 0".to_string()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -300,7 +300,7 @@ pub struct ReverseProxyConfig {
|
||||
}
|
||||
|
||||
impl ReverseProxyConfig {
|
||||
fn prepare(&mut self, working_dir: &str) -> Result<(), M3uFilterError> {
|
||||
fn prepare(&mut self, working_dir: &str) -> Result<(), TuliProxError> {
|
||||
if let Some(stream) = self.stream.as_mut() {
|
||||
stream.prepare()?;
|
||||
}
|
||||
@@ -398,26 +398,26 @@ pub struct Config {
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub async fn set_api_proxy(&mut self, api_proxy: Option<ApiProxyConfig>) -> Result<(), M3uFilterError> {
|
||||
pub async fn set_api_proxy(&mut self, api_proxy: Option<ApiProxyConfig>) -> Result<(), TuliProxError> {
|
||||
self.t_api_proxy = Arc::new(RwLock::new(api_proxy));
|
||||
self.check_target_user().await
|
||||
}
|
||||
|
||||
async fn check_username(&self, output_username: Option<&str>, target_name: &str) -> Result<(), M3uFilterError> {
|
||||
async fn check_username(&self, output_username: Option<&str>, target_name: &str) -> Result<(), TuliProxError> {
|
||||
if let Some(username) = output_username {
|
||||
if let Some((_, config_target)) = self.get_target_for_username(username).await {
|
||||
if config_target.name != target_name {
|
||||
return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "User:{username} does not belong to target: {}", target_name);
|
||||
return create_tuliprox_error_result!(TuliProxErrorKind::Info, "User:{username} does not belong to target: {}", target_name);
|
||||
}
|
||||
} else {
|
||||
return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "User: {username} does not exist");
|
||||
return create_tuliprox_error_result!(TuliProxErrorKind::Info, "User: {username} does not exist");
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
async fn check_target_user(&mut self) -> Result<(), M3uFilterError> {
|
||||
async fn check_target_user(&mut self) -> Result<(), TuliProxError> {
|
||||
let check_homerun = self.hdhomerun.as_ref().is_some_and(|h| h.enabled);
|
||||
for source in &self.sources {
|
||||
for target in &source.targets {
|
||||
@@ -566,26 +566,26 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_unique_input_names(&mut self) -> Result<(), M3uFilterError> {
|
||||
fn check_unique_input_names(&mut self) -> Result<(), TuliProxError> {
|
||||
let mut seen_names = HashSet::new();
|
||||
for source in &mut self.sources {
|
||||
for input in &source.inputs {
|
||||
let input_name = input.name.trim().to_string();
|
||||
if input_name.is_empty() {
|
||||
return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "input name required");
|
||||
return create_tuliprox_error_result!(TuliProxErrorKind::Info, "input name required");
|
||||
}
|
||||
if seen_names.contains(input_name.as_str()) {
|
||||
return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "input names should be unique: {}", input_name);
|
||||
return create_tuliprox_error_result!(TuliProxErrorKind::Info, "input names should be unique: {}", input_name);
|
||||
}
|
||||
seen_names.insert(input_name);
|
||||
if let Some(aliases) = &input.aliases {
|
||||
for alias in aliases {
|
||||
let input_name = alias.name.trim().to_string();
|
||||
if input_name.is_empty() {
|
||||
return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "input name required");
|
||||
return create_tuliprox_error_result!(TuliProxErrorKind::Info, "input name required");
|
||||
}
|
||||
if seen_names.contains(input_name.as_str()) {
|
||||
return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "input names should be unique: {}", input_name);
|
||||
return create_tuliprox_error_result!(TuliProxErrorKind::Info, "input names should be unique: {}", input_name);
|
||||
}
|
||||
seen_names.insert(input_name);
|
||||
}
|
||||
@@ -595,7 +595,7 @@ impl Config {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_unique_target_names(&mut self) -> Result<HashSet<String>, M3uFilterError> {
|
||||
fn check_unique_target_names(&mut self) -> Result<HashSet<String>, TuliProxError> {
|
||||
let mut seen_names = HashSet::new();
|
||||
let default_target_name = default_as_default();
|
||||
for source in &self.sources {
|
||||
@@ -603,11 +603,11 @@ impl Config {
|
||||
// check target name is unique
|
||||
let target_name = target.name.trim().to_string();
|
||||
if target_name.is_empty() {
|
||||
return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "target name required");
|
||||
return create_tuliprox_error_result!(TuliProxErrorKind::Info, "target name required");
|
||||
}
|
||||
if !default_target_name.eq_ignore_ascii_case(target_name.as_str()) {
|
||||
if seen_names.contains(target_name.as_str()) {
|
||||
return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "target names should be unique: {}", target_name);
|
||||
return create_tuliprox_error_result!(TuliProxErrorKind::Info, "target names should be unique: {}", target_name);
|
||||
}
|
||||
seen_names.insert(target_name);
|
||||
}
|
||||
@@ -616,13 +616,13 @@ impl Config {
|
||||
Ok(seen_names)
|
||||
}
|
||||
|
||||
fn check_scheduled_targets(&mut self, target_names: &HashSet<String>) -> Result<(), M3uFilterError> {
|
||||
fn check_scheduled_targets(&mut self, target_names: &HashSet<String>) -> Result<(), TuliProxError> {
|
||||
if let Some(schedules) = &self.schedules {
|
||||
for schedule in schedules {
|
||||
if let Some(targets) = &schedule.targets {
|
||||
for target_name in targets {
|
||||
if !target_names.contains(target_name) {
|
||||
return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "Unknown target name in scheduler: {}", target_name);
|
||||
return create_tuliprox_error_result!(TuliProxErrorKind::Info, "Unknown target name in scheduler: {}", target_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -634,12 +634,12 @@ impl Config {
|
||||
/**
|
||||
* if `include_computed` set to true for `app_state`
|
||||
*/
|
||||
pub fn prepare(&mut self, include_computed: bool) -> Result<(), M3uFilterError> {
|
||||
pub fn prepare(&mut self, include_computed: bool) -> Result<(), TuliProxError> {
|
||||
let work_dir = &self.working_dir;
|
||||
self.working_dir = file_utils::get_working_path(work_dir);
|
||||
if include_computed {
|
||||
self.t_access_token_secret = generate_secret();
|
||||
self.t_encrypt_secret = <&[u8] as TryInto<[u8; 16]>>::try_into(&generate_secret()[0..16]).map_err(|err| M3uFilterError::new(M3uFilterErrorKind::Info, err.to_string()))?;
|
||||
self.t_encrypt_secret = <&[u8] as TryInto<[u8; 16]>>::try_into(&generate_secret()[0..16]).map_err(|err| TuliProxError::new(TuliProxErrorKind::Info, err.to_string()))?;
|
||||
self.prepare_custom_stream_response();
|
||||
}
|
||||
self.prepare_directories();
|
||||
@@ -678,7 +678,7 @@ impl Config {
|
||||
set_directory(&mut self.user_config_dir, "user_config", &self.working_dir);
|
||||
}
|
||||
|
||||
fn prepare_hdhomerun(&mut self) -> Result<(), M3uFilterError> {
|
||||
fn prepare_hdhomerun(&mut self) -> Result<(), TuliProxError> {
|
||||
if let Some(hdhomerun) = self.hdhomerun.as_mut() {
|
||||
if hdhomerun.enabled {
|
||||
hdhomerun.prepare(self.api.port)?;
|
||||
@@ -687,7 +687,7 @@ impl Config {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prepare_sources(&mut self, include_computed: bool) -> Result<(), M3uFilterError> {
|
||||
fn prepare_sources(&mut self, include_computed: bool) -> Result<(), TuliProxError> {
|
||||
// prepare sources and set id's
|
||||
let mut source_index: u16 = 1;
|
||||
let mut target_index: u16 = 1;
|
||||
@@ -706,7 +706,7 @@ impl Config {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prepare_templates(&mut self) -> Result<(), M3uFilterError> {
|
||||
fn prepare_templates(&mut self) -> Result<(), TuliProxError> {
|
||||
if let Some(templates) = &mut self.templates {
|
||||
match prepare_templates(templates) {
|
||||
Ok(tmplts) => {
|
||||
@@ -720,14 +720,14 @@ impl Config {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prepare_web(&mut self) -> Result<(), M3uFilterError> {
|
||||
fn prepare_web(&mut self) -> Result<(), TuliProxError> {
|
||||
if let Some(web_ui_config) = self.web_ui.as_mut() {
|
||||
web_ui_config.prepare(&self.t_config_path)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prepare_video_config(&mut self) -> Result<(), M3uFilterError> {
|
||||
fn prepare_video_config(&mut self) -> Result<(), TuliProxError> {
|
||||
match &mut self.video {
|
||||
None => {
|
||||
self.video = Some(VideoConfig {
|
||||
@@ -793,7 +793,7 @@ impl Config {
|
||||
/// * `target_args` the program parameters given with `-target` parameter.
|
||||
/// * `sources` configured sources in config file
|
||||
///
|
||||
pub fn validate_targets(target_args: Option<&Vec<String>>, sources: &Vec<ConfigSource>) -> Result<ProcessTargets, M3uFilterError> {
|
||||
pub fn validate_targets(target_args: Option<&Vec<String>>, sources: &Vec<ConfigSource>) -> Result<ProcessTargets, TuliProxError> {
|
||||
let mut enabled = true;
|
||||
let mut inputs: Vec<u16> = vec![];
|
||||
let mut targets: Vec<u16> = vec![];
|
||||
@@ -820,7 +820,7 @@ pub fn validate_targets(target_args: Option<&Vec<String>>, sources: &Vec<ConfigS
|
||||
|
||||
let missing_targets: Vec<String> = check_targets.iter().filter(|&(_, v)| *v == 0).map(|(k, _)| k.to_string()).collect();
|
||||
if !missing_targets.is_empty() {
|
||||
return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "No target found for {}", missing_targets.join(", "));
|
||||
return create_tuliprox_error_result!(TuliProxErrorKind::Info, "No target found for {}", missing_targets.join(", "));
|
||||
}
|
||||
// let processing_targets: Vec<String> = check_targets.iter().filter(|&(_, v)| *v != 0).map(|(k, _)| k.to_string()).collect();
|
||||
// info!("Processing targets {}", processing_targets.join(", "));
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::m3u_filter_error::{create_m3u_filter_error_result, M3uFilterError, M3uFilterErrorKind};
|
||||
use crate::tuliprox_error::{create_tuliprox_error_result, TuliProxError, TuliProxErrorKind};
|
||||
use regex::Regex;
|
||||
use std::borrow::BorrowMut;
|
||||
use std::collections::HashMap;
|
||||
@@ -35,7 +35,7 @@ impl VideoConfig {
|
||||
/// # Panics
|
||||
///
|
||||
/// Will panic if default `RegEx` gets invalid
|
||||
pub fn prepare(&mut self) -> Result<(), M3uFilterError> {
|
||||
pub fn prepare(&mut self) -> Result<(), TuliProxError> {
|
||||
if self.extensions.is_empty() {
|
||||
self.extensions = ["mkv", "avi", "mp4", "mpeg", "divx", "mov"]
|
||||
.iter()
|
||||
@@ -57,7 +57,7 @@ impl VideoConfig {
|
||||
downl.t_re_episode_pattern = Some(pattern);
|
||||
}
|
||||
Err(err) => {
|
||||
return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "cant parse regex: {episode_pattern} {err}");
|
||||
return create_tuliprox_error_result!(TuliProxErrorKind::Info, "cant parse regex: {episode_pattern} {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use log::warn;
|
||||
use regex::Regex;
|
||||
use crate::m3u_filter_error::{M3uFilterError, M3uFilterErrorKind, create_m3u_filter_error_result};
|
||||
use crate::tuliprox_error::{TuliProxError, TuliProxErrorKind, create_tuliprox_error_result};
|
||||
use crate::utils::CONSTANTS;
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
@@ -57,7 +57,7 @@ impl EpgSmartMatchConfig {
|
||||
/// let config = EpgSmartMatchConfig::new().unwrap();
|
||||
/// assert!(config.enabled);
|
||||
/// ```
|
||||
pub fn new() -> Result<Self, M3uFilterError> {
|
||||
pub fn new() -> Result<Self, TuliProxError> {
|
||||
let mut this = Self { enabled: true, ..Self::default() };
|
||||
this.prepare()?;
|
||||
Ok(this)
|
||||
@@ -71,8 +71,8 @@ impl EpgSmartMatchConfig {
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// `Ok(())` if preparation succeeds, or an `M3uFilterError` if regex compilation fails.
|
||||
pub fn prepare(&mut self) -> Result<(), M3uFilterError> {
|
||||
/// `Ok(())` if preparation succeeds, or an `TuliProxError` if regex compilation fails.
|
||||
pub fn prepare(&mut self) -> Result<(), TuliProxError> {
|
||||
if !self.enabled {
|
||||
return Ok(());
|
||||
}
|
||||
@@ -99,7 +99,7 @@ impl EpgSmartMatchConfig {
|
||||
Some(regstr) => {
|
||||
let re = regex::Regex::new(regstr.as_str());
|
||||
if re.is_err() {
|
||||
return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "cant parse regex: {}", regstr);
|
||||
return create_tuliprox_error_result!(TuliProxErrorKind::Info, "cant parse regex: {}", regstr);
|
||||
}
|
||||
Some(re.unwrap())
|
||||
}
|
||||
@@ -148,7 +148,7 @@ pub struct EpgConfig {
|
||||
}
|
||||
|
||||
impl EpgConfig {
|
||||
pub fn prepare(&mut self, include_computed: bool) -> Result<(), M3uFilterError> {
|
||||
pub fn prepare(&mut self, include_computed: bool) -> Result<(), TuliProxError> {
|
||||
if include_computed {
|
||||
self.t_urls = self.url.as_ref().map_or_else(Vec::new, |epg_url| {
|
||||
match epg_url {
|
||||
|
||||
+13
-13
@@ -1,4 +1,4 @@
|
||||
use crate::m3u_filter_error::{create_m3u_filter_error_result, handle_m3u_filter_error_result_list, info_err, M3uFilterError, M3uFilterErrorKind};
|
||||
use crate::tuliprox_error::{create_tuliprox_error_result, handle_tuliprox_error_result_list, info_err, TuliProxError, TuliProxErrorKind};
|
||||
use crate::model::EpgConfig;
|
||||
use crate::utils::config_reader::csv_read_inputs;
|
||||
use crate::utils::default_as_true;
|
||||
@@ -82,9 +82,9 @@ impl Display for InputType {
|
||||
}
|
||||
|
||||
impl FromStr for InputType {
|
||||
type Err = M3uFilterError;
|
||||
type Err = TuliProxError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, M3uFilterError> {
|
||||
fn from_str(s: &str) -> Result<Self, TuliProxError> {
|
||||
if s.eq(Self::M3U) {
|
||||
Ok(Self::M3u)
|
||||
} else if s.eq(Self::XTREAM) {
|
||||
@@ -94,7 +94,7 @@ impl FromStr for InputType {
|
||||
} else if s.eq(Self::XTREAM_BATCH) {
|
||||
Ok(Self::XtreamBatch)
|
||||
} else {
|
||||
create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "Unknown InputType: {}", s)
|
||||
create_tuliprox_error_result!(TuliProxErrorKind::Info, "Unknown InputType: {}", s)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -131,15 +131,15 @@ impl Display for InputFetchMethod {
|
||||
}
|
||||
|
||||
impl FromStr for InputFetchMethod {
|
||||
type Err = M3uFilterError;
|
||||
type Err = TuliProxError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, M3uFilterError> {
|
||||
fn from_str(s: &str) -> Result<Self, TuliProxError> {
|
||||
if s.eq(Self::GET_METHOD) {
|
||||
Ok(Self::GET)
|
||||
} else if s.eq(Self::POST_METHOD) {
|
||||
Ok(Self::POST)
|
||||
} else {
|
||||
create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "Unknown Fetch Method: {}", s)
|
||||
create_tuliprox_error_result!(TuliProxErrorKind::Info, "Unknown Fetch Method: {}", s)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -213,7 +213,7 @@ pub struct ConfigInputAlias {
|
||||
|
||||
|
||||
impl ConfigInputAlias {
|
||||
pub fn prepare(&mut self, index: u16, input_type: &InputType) -> Result<(), M3uFilterError> {
|
||||
pub fn prepare(&mut self, index: u16, input_type: &InputType) -> Result<(), TuliProxError> {
|
||||
self.id = index;
|
||||
self.name = self.name.trim().to_string();
|
||||
if self.name.is_empty() {
|
||||
@@ -275,7 +275,7 @@ pub struct ConfigInput {
|
||||
|
||||
impl ConfigInput {
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
pub fn prepare(&mut self, index: u16, include_computed: bool) -> Result<u16, M3uFilterError> {
|
||||
pub fn prepare(&mut self, index: u16, include_computed: bool) -> Result<u16, TuliProxError> {
|
||||
self.id = index;
|
||||
self.check_url()?;
|
||||
self.prepare_batch()?;
|
||||
@@ -318,12 +318,12 @@ impl ConfigInput {
|
||||
|
||||
if let Some(aliases) = self.aliases.as_mut() {
|
||||
let input_type = &self.input_type;
|
||||
handle_m3u_filter_error_result_list!(M3uFilterErrorKind::Info, aliases.iter_mut().enumerate().map(|(idx, i)| i.prepare(index+1+(idx as u16), input_type)));
|
||||
handle_tuliprox_error_result_list!(TuliProxErrorKind::Info, aliases.iter_mut().enumerate().map(|(idx, i)| i.prepare(index+1+(idx as u16), input_type)));
|
||||
}
|
||||
Ok(index + self.aliases.as_ref().map_or(0, std::vec::Vec::len) as u16)
|
||||
}
|
||||
|
||||
fn check_url(&mut self) -> Result<(), M3uFilterError> {
|
||||
fn check_url(&mut self) -> Result<(), TuliProxError> {
|
||||
self.url = self.url.trim().to_string();
|
||||
if self.url.is_empty() {
|
||||
return Err(info_err!("url for input is mandatory".to_string()));
|
||||
@@ -331,7 +331,7 @@ impl ConfigInput {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prepare_batch(&mut self) -> Result<(), M3uFilterError> {
|
||||
fn prepare_batch(&mut self) -> Result<(), TuliProxError> {
|
||||
if self.input_type == InputType::M3uBatch || self.input_type == InputType::XtreamBatch {
|
||||
let input_type = if self.input_type == InputType::M3uBatch {
|
||||
InputType::M3u
|
||||
@@ -364,7 +364,7 @@ impl ConfigInput {
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(M3uFilterError::new(M3uFilterErrorKind::Info, err.to_string()));
|
||||
return Err(TuliProxError::new(TuliProxErrorKind::Info, err.to_string()));
|
||||
}
|
||||
}
|
||||
self.input_type = input_type;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use regex::Regex;
|
||||
use crate::m3u_filter_error::{M3uFilterError, M3uFilterErrorKind};
|
||||
use crate::tuliprox_error::{TuliProxError, TuliProxErrorKind};
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
@@ -31,7 +31,7 @@ pub struct IpCheckConfig {
|
||||
}
|
||||
|
||||
impl IpCheckConfig {
|
||||
pub fn prepare(&mut self) -> Result<(), M3uFilterError> {
|
||||
pub fn prepare(&mut self) -> Result<(), TuliProxError> {
|
||||
if let Some(url) = &self.url {
|
||||
let url = url.trim();
|
||||
if url.is_empty() {
|
||||
@@ -58,22 +58,22 @@ impl IpCheckConfig {
|
||||
}
|
||||
|
||||
if self.url.is_none() && self.url_ipv4.is_none() && self.url_ipv6.is_none() {
|
||||
return Err(M3uFilterError::new(M3uFilterErrorKind::Info, "No url provided!".to_owned()));
|
||||
return Err(TuliProxError::new(TuliProxErrorKind::Info, "No url provided!".to_owned()));
|
||||
}
|
||||
|
||||
// TODO allow or do not allow ?
|
||||
// if self.url.is_some() && (self.url_ipv4.is_some() || self.url_ipv6.is_some()) {
|
||||
// return Err(M3uFilterError::new(M3uFilterErrorKind::Info, "url in combination with ipv4 and/or ipv6 url not allowed!".to_owned()));
|
||||
// return Err(TuliProxError::new(TuliProxErrorKind::Info, "url in combination with ipv4 and/or ipv6 url not allowed!".to_owned()));
|
||||
// }
|
||||
|
||||
if let Some(p4) = &self.pattern_ipv4 {
|
||||
self.t_pattern_ipv4 = Some(
|
||||
Regex::new(p4).map_err(|err| M3uFilterError::new(M3uFilterErrorKind::Info, format!("Invalid IPv4 regex: {p4} {err}")))?,
|
||||
Regex::new(p4).map_err(|err| TuliProxError::new(TuliProxErrorKind::Info, format!("Invalid IPv4 regex: {p4} {err}")))?,
|
||||
);
|
||||
}
|
||||
if let Some(p6) = &self.pattern_ipv6 {
|
||||
self.t_pattern_ipv6 = Some(
|
||||
Regex::new(p6).map_err(|err| M3uFilterError::new(M3uFilterErrorKind::Info, format!("Invalid IPv6 regex: {p6} {err}")))?,
|
||||
Regex::new(p6).map_err(|err| TuliProxError::new(TuliProxErrorKind::Info, format!("Invalid IPv6 regex: {p6} {err}")))?,
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::m3u_filter_error::{M3uFilterError, M3uFilterErrorKind};
|
||||
use crate::tuliprox_error::{TuliProxError, TuliProxErrorKind};
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
@@ -9,24 +9,24 @@ pub struct ProxyConfig {
|
||||
}
|
||||
|
||||
impl ProxyConfig {
|
||||
pub(crate) fn prepare(&mut self) -> Result<(), M3uFilterError> {
|
||||
pub(crate) fn prepare(&mut self) -> Result<(), TuliProxError> {
|
||||
if self.username.is_some() || self.password.is_some() {
|
||||
if let (Some(username), Some(password)) = (self.username.as_ref(), self.password.as_ref()) {
|
||||
let uname = username.trim();
|
||||
let pwd = password.trim();
|
||||
if uname.is_empty() || pwd.is_empty() {
|
||||
return Err(M3uFilterError::new(M3uFilterErrorKind::Info, "Proxy credentials missing".to_string()));
|
||||
return Err(TuliProxError::new(TuliProxErrorKind::Info, "Proxy credentials missing".to_string()));
|
||||
}
|
||||
self.username = Some(uname.to_string());
|
||||
self.password = Some(pwd.to_string());
|
||||
} else {
|
||||
return Err(M3uFilterError::new(M3uFilterErrorKind::Info, "Proxy credentials missing".to_string()));
|
||||
return Err(TuliProxError::new(TuliProxErrorKind::Info, "Proxy credentials missing".to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
self.url = self.url.trim().to_string();
|
||||
if self.url.is_empty() {
|
||||
return Err(M3uFilterError::new(M3uFilterErrorKind::Info, "Proxy url missing".to_string()));
|
||||
return Err(TuliProxError::new(TuliProxErrorKind::Info, "Proxy url missing".to_string()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::m3u_filter_error::{M3uFilterError, M3uFilterErrorKind, create_m3u_filter_error_result};
|
||||
use crate::tuliprox_error::{TuliProxError, TuliProxErrorKind, create_tuliprox_error_result};
|
||||
use crate::model::ItemField;
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
@@ -12,13 +12,13 @@ pub struct ConfigRename {
|
||||
}
|
||||
|
||||
impl ConfigRename {
|
||||
pub fn prepare(&mut self) -> Result<(), M3uFilterError> {
|
||||
pub fn prepare(&mut self) -> Result<(), TuliProxError> {
|
||||
match regex::Regex::new(&self.pattern) {
|
||||
Ok(pattern) => {
|
||||
self.re = Some(pattern);
|
||||
Ok(())
|
||||
}
|
||||
Err(err) => create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "cant parse regex: {} {err}", &self.pattern),
|
||||
Err(err) => create_tuliprox_error_result!(TuliProxErrorKind::Info, "cant parse regex: {} {err}", &self.pattern),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
use regex::Regex;
|
||||
use crate::m3u_filter_error::{M3uFilterError, M3uFilterErrorKind, create_m3u_filter_error, handle_m3u_filter_error_result_list};
|
||||
use crate::tuliprox_error::{TuliProxError, TuliProxErrorKind, create_tuliprox_error, handle_tuliprox_error_result_list};
|
||||
use crate::model::{ItemField};
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
@@ -10,12 +10,12 @@ pub enum SortOrder {
|
||||
Desc,
|
||||
}
|
||||
|
||||
fn compile_regex_vec(patterns: Option<&Vec<String>>) -> Result<Option<Vec<Regex>>, M3uFilterError> {
|
||||
fn compile_regex_vec(patterns: Option<&Vec<String>>) -> Result<Option<Vec<Regex>>, TuliProxError> {
|
||||
patterns.as_ref()
|
||||
.map(|seq| {
|
||||
seq.iter()
|
||||
.map(|s| Regex::new(s).map_err(|err| {
|
||||
create_m3u_filter_error!(M3uFilterErrorKind::Info, "cant parse regex: {s} {err}")
|
||||
create_tuliprox_error!(TuliProxErrorKind::Info, "cant parse regex: {s} {err}")
|
||||
}))
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
})
|
||||
@@ -34,7 +34,7 @@ pub struct ConfigSortGroup {
|
||||
|
||||
|
||||
impl ConfigSortGroup {
|
||||
pub fn prepare(&mut self) -> Result<(), M3uFilterError> {
|
||||
pub fn prepare(&mut self) -> Result<(), TuliProxError> {
|
||||
// Compile sequence patterns, if any
|
||||
self.t_sequence = compile_regex_vec(self.sequence.as_ref())?;
|
||||
Ok(())
|
||||
@@ -58,11 +58,11 @@ pub struct ConfigSortChannel {
|
||||
}
|
||||
|
||||
impl ConfigSortChannel {
|
||||
pub fn prepare(&mut self) -> Result<(), M3uFilterError> {
|
||||
pub fn prepare(&mut self) -> Result<(), TuliProxError> {
|
||||
// Compile group_pattern
|
||||
self.t_re_group_pattern = Some(
|
||||
Regex::new(&self.group_pattern).map_err(|err| {
|
||||
create_m3u_filter_error!(M3uFilterErrorKind::Info, "cant parse regex: {} {err}", &self.group_pattern)
|
||||
create_tuliprox_error!(TuliProxErrorKind::Info, "cant parse regex: {} {err}", &self.group_pattern)
|
||||
})?
|
||||
);
|
||||
|
||||
@@ -84,12 +84,12 @@ pub struct ConfigSort {
|
||||
}
|
||||
|
||||
impl ConfigSort {
|
||||
pub fn prepare(&mut self) -> Result<(), M3uFilterError> {
|
||||
pub fn prepare(&mut self) -> Result<(), TuliProxError> {
|
||||
if let Some(group) = self.groups.as_mut() {
|
||||
group.prepare()?;
|
||||
}
|
||||
if let Some(channels) = self.channels.as_mut() {
|
||||
handle_m3u_filter_error_result_list!(M3uFilterErrorKind::Info, channels.iter_mut().map(ConfigSortChannel::prepare));
|
||||
handle_tuliprox_error_result_list!(TuliProxErrorKind::Info, channels.iter_mut().map(ConfigSortChannel::prepare));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::m3u_filter_error::{M3uFilterError, M3uFilterErrorKind, handle_m3u_filter_error_result_list};
|
||||
use crate::tuliprox_error::{TuliProxError, TuliProxErrorKind, handle_tuliprox_error_result_list};
|
||||
use crate::model::ConfigInput;
|
||||
use crate::model::config_target::ConfigTarget;
|
||||
|
||||
@@ -11,8 +11,8 @@ pub struct ConfigSource {
|
||||
|
||||
impl ConfigSource {
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
pub fn prepare(&mut self, index: u16, include_computed: bool) -> Result<u16, M3uFilterError> {
|
||||
handle_m3u_filter_error_result_list!(M3uFilterErrorKind::Info, self.inputs.iter_mut().enumerate().map(|(idx, i)| i.prepare(index+(idx as u16), include_computed)));
|
||||
pub fn prepare(&mut self, index: u16, include_computed: bool) -> Result<u16, TuliProxError> {
|
||||
handle_tuliprox_error_result_list!(TuliProxErrorKind::Info, self.inputs.iter_mut().enumerate().map(|(idx, i)| i.prepare(index+(idx as u16), include_computed)));
|
||||
Ok(index + (self.inputs.len() as u16))
|
||||
}
|
||||
|
||||
|
||||
+15
-15
@@ -1,5 +1,5 @@
|
||||
use crate::foundation::filter::{get_filter, Filter, MockValueProcessor, PatternTemplate, ValueProvider};
|
||||
use crate::m3u_filter_error::{create_m3u_filter_error_result, handle_m3u_filter_error_result, handle_m3u_filter_error_result_list, info_err, M3uFilterError, M3uFilterErrorKind};
|
||||
use crate::tuliprox_error::{create_tuliprox_error_result, handle_tuliprox_error_result, handle_tuliprox_error_result_list, info_err, TuliProxError, TuliProxErrorKind};
|
||||
use crate::model::cluster_flags::ClusterFlags;
|
||||
use crate::model::config_rename::ConfigRename;
|
||||
use crate::model::config_sort::ConfigSort;
|
||||
@@ -190,7 +190,7 @@ pub struct ConfigTarget {
|
||||
}
|
||||
|
||||
impl ConfigTarget {
|
||||
pub fn prepare(&mut self, id: u16, templates: Option<&Vec<PatternTemplate>>) -> Result<(), M3uFilterError> {
|
||||
pub fn prepare(&mut self, id: u16, templates: Option<&Vec<PatternTemplate>>) -> Result<(), TuliProxError> {
|
||||
self.id = id;
|
||||
if self.output.is_empty() {
|
||||
return Err(info_err!(format!("Missing output format for {}", self.name)));
|
||||
@@ -208,7 +208,7 @@ impl ConfigTarget {
|
||||
TargetOutput::Xtream(_) => {
|
||||
xtream_cnt += 1;
|
||||
if default_as_default().eq_ignore_ascii_case(&self.name) {
|
||||
return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "unique target name is required for xtream type output: {}", self.name);
|
||||
return create_tuliprox_error_result!(TuliProxErrorKind::Info, "unique target name is required for xtream type output: {}", self.name);
|
||||
}
|
||||
}
|
||||
TargetOutput::M3u(m3u_output) => {
|
||||
@@ -219,7 +219,7 @@ impl ConfigTarget {
|
||||
strm_cnt += 1;
|
||||
strm_output.directory = strm_output.directory.trim().to_string();
|
||||
if strm_output.directory.trim().is_empty() {
|
||||
return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "directory is required for strm type: {}", self.name);
|
||||
return create_tuliprox_error_result!(TuliProxErrorKind::Info, "directory is required for strm type: {}", self.name);
|
||||
}
|
||||
if let Some(username) = &mut strm_output.username {
|
||||
*username = username.trim().to_string();
|
||||
@@ -234,19 +234,19 @@ impl ConfigTarget {
|
||||
hdhr_cnt += 1;
|
||||
hdhomerun_output.username = hdhomerun_output.username.trim().to_string();
|
||||
if hdhomerun_output.username.is_empty() {
|
||||
return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "Username is required for HdHomeRun type: {}", self.name);
|
||||
return create_tuliprox_error_result!(TuliProxErrorKind::Info, "Username is required for HdHomeRun type: {}", self.name);
|
||||
}
|
||||
|
||||
hdhomerun_output.device = hdhomerun_output.device.trim().to_string();
|
||||
if hdhomerun_output.device.is_empty() {
|
||||
return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "Device is required for HdHomeRun type: {}", self.name);
|
||||
return create_tuliprox_error_result!(TuliProxErrorKind::Info, "Device is required for HdHomeRun type: {}", self.name);
|
||||
}
|
||||
|
||||
if let Some(use_output) = hdhomerun_output.use_output.as_ref() {
|
||||
match &use_output {
|
||||
TargetType::M3u => { hdhomerun_needs_m3u = true; }
|
||||
TargetType::Xtream => { hdhomerun_needs_xtream = true; }
|
||||
_ => return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "HdHomeRun output option `use_output` only accepts `m3u` or `xtream` for target: {}", self.name),
|
||||
_ => return create_tuliprox_error_result!(TuliProxErrorKind::Info, "HdHomeRun output option `use_output` only accepts `m3u` or `xtream` for target: {}", self.name),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -254,22 +254,22 @@ impl ConfigTarget {
|
||||
}
|
||||
|
||||
if m3u_cnt > 1 || strm_cnt > 1 || xtream_cnt > 1 || hdhr_cnt > 1 {
|
||||
return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "Multiple output formats with same type : {}", self.name);
|
||||
return create_tuliprox_error_result!(TuliProxErrorKind::Info, "Multiple output formats with same type : {}", self.name);
|
||||
}
|
||||
|
||||
if strm_cnt > 0 && strm_needs_xtream && xtream_cnt == 0 {
|
||||
return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "strm output with a username is only permitted when used in combination with xtream output: {}", self.name);
|
||||
return create_tuliprox_error_result!(TuliProxErrorKind::Info, "strm output with a username is only permitted when used in combination with xtream output: {}", self.name);
|
||||
}
|
||||
|
||||
if hdhr_cnt > 0 {
|
||||
if xtream_cnt == 0 && m3u_cnt == 0 {
|
||||
return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "HdHomeRun output is only permitted when used in combination with xtream or m3u output: {}", self.name);
|
||||
return create_tuliprox_error_result!(TuliProxErrorKind::Info, "HdHomeRun output is only permitted when used in combination with xtream or m3u output: {}", self.name);
|
||||
}
|
||||
if hdhomerun_needs_m3u && m3u_cnt == 0 {
|
||||
return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "HdHomeRun output has `use_output=m3u` but no `m3u` output defined: {}", self.name);
|
||||
return create_tuliprox_error_result!(TuliProxErrorKind::Info, "HdHomeRun output has `use_output=m3u` but no `m3u` output defined: {}", self.name);
|
||||
}
|
||||
if hdhomerun_needs_xtream && xtream_cnt == 0 {
|
||||
return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "HdHomeRun output has `use_output=xtream` but no `xtream` output defined: {}", self.name);
|
||||
return create_tuliprox_error_result!(TuliProxErrorKind::Info, "HdHomeRun output has `use_output=xtream` but no `xtream` output defined: {}", self.name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,7 +278,7 @@ impl ConfigTarget {
|
||||
match regexps {
|
||||
Ok(watch_re) => self.t_watch_re = Some(watch_re),
|
||||
Err(err) => {
|
||||
return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "Invalid watch regular expression: {}", err);
|
||||
return create_tuliprox_error_result!(TuliProxErrorKind::Info, "Invalid watch regular expression: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -288,10 +288,10 @@ impl ConfigTarget {
|
||||
// debug!("Filter: {}", fltr);
|
||||
self.t_filter = Some(fltr);
|
||||
if let Some(renames) = self.rename.as_mut() {
|
||||
handle_m3u_filter_error_result_list!(M3uFilterErrorKind::Info, renames.iter_mut().map(ConfigRename::prepare));
|
||||
handle_tuliprox_error_result_list!(TuliProxErrorKind::Info, renames.iter_mut().map(ConfigRename::prepare));
|
||||
}
|
||||
if let Some(sort) = self.sort.as_mut() {
|
||||
handle_m3u_filter_error_result!(M3uFilterErrorKind::Info, sort.prepare());
|
||||
handle_tuliprox_error_result!(TuliProxErrorKind::Info, sort.prepare());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::fs::File;
|
||||
use std::io::BufRead;
|
||||
use std::path::PathBuf;
|
||||
use crate::auth::user::UserCredential;
|
||||
use crate::m3u_filter_error::{M3uFilterError, M3uFilterErrorKind, create_m3u_filter_error_result};
|
||||
use crate::tuliprox_error::{TuliProxError, TuliProxErrorKind, create_tuliprox_error_result};
|
||||
use crate::utils::file_utils;
|
||||
use crate::utils::file_utils::file_reader;
|
||||
|
||||
@@ -21,7 +21,7 @@ pub struct WebAuthConfig {
|
||||
}
|
||||
|
||||
impl WebAuthConfig {
|
||||
pub fn prepare(&mut self, config_path: &str) -> Result<(), M3uFilterError> {
|
||||
pub fn prepare(&mut self, config_path: &str) -> Result<(), TuliProxError> {
|
||||
let userfile_name = self.userfile.as_ref().map_or_else(|| file_utils::get_default_user_file_path(config_path), std::borrow::ToOwned::to_owned);
|
||||
self.userfile = Some(userfile_name.clone());
|
||||
|
||||
@@ -29,7 +29,7 @@ impl WebAuthConfig {
|
||||
if !file_utils::path_exists(&userfile_path) {
|
||||
userfile_path = PathBuf::from(config_path).join(&userfile_name);
|
||||
if !file_utils::path_exists(&userfile_path) {
|
||||
return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "Could not find userfile {}", &userfile_name);
|
||||
return create_tuliprox_error_result!(TuliProxErrorKind::Info, "Could not find userfile {}", &userfile_name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ impl WebAuthConfig {
|
||||
|
||||
self.t_users = Some(users);
|
||||
} else {
|
||||
return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "Could not read userfile {:?}", &userfile_path);
|
||||
return create_tuliprox_error_result!(TuliProxErrorKind::Info, "Could not read userfile {:?}", &userfile_path);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::utils::default_as_true;
|
||||
use crate::m3u_filter_error::{M3uFilterError, M3uFilterErrorKind};
|
||||
use crate::tuliprox_error::{TuliProxError, TuliProxErrorKind};
|
||||
use crate::model::WebAuthConfig;
|
||||
|
||||
const RESERVED_PATHS: &[&str] = &[
|
||||
@@ -24,7 +24,7 @@ pub struct WebUiConfig {
|
||||
}
|
||||
|
||||
impl WebUiConfig {
|
||||
pub fn prepare(&mut self, config_path: &str) -> Result<(), M3uFilterError> {
|
||||
pub fn prepare(&mut self, config_path: &str) -> Result<(), TuliProxError> {
|
||||
if !self.enabled {
|
||||
self.auth = None;
|
||||
}
|
||||
@@ -36,7 +36,7 @@ impl WebUiConfig {
|
||||
} else {
|
||||
let web_path = web_path.trim().trim_start_matches('/').trim_end_matches('/').to_string();
|
||||
if RESERVED_PATHS.contains(&web_path.to_lowercase().as_str()) {
|
||||
return Err(M3uFilterError::new(M3uFilterErrorKind::Info, format!("web ui path is a reserved path. Do not use {RESERVED_PATHS:?}")));
|
||||
return Err(TuliProxError::new(TuliProxErrorKind::Info, format!("web ui path is a reserved path. Do not use {RESERVED_PATHS:?}")));
|
||||
}
|
||||
self.path = Some(web_path);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::collections::HashSet;
|
||||
use log::warn;
|
||||
use crate::m3u_filter_error::{M3uFilterError, M3uFilterErrorKind, create_m3u_filter_error_result};
|
||||
fn default_friendly_name() -> String { String::from("M3uFilterTV") }
|
||||
use crate::tuliprox_error::{TuliProxError, TuliProxErrorKind, create_tuliprox_error_result};
|
||||
fn default_friendly_name() -> String { String::from("TuliProxTV") }
|
||||
fn default_manufacturer() -> String { String::from("Silicondust") }
|
||||
fn default_model_name() -> String { String::from("HDTC-2US") }
|
||||
fn default_firmware_name() -> String { String::from("hdhomeruntc_atsc") }
|
||||
@@ -72,7 +72,7 @@ pub struct HdHomeRunConfig {
|
||||
}
|
||||
|
||||
impl HdHomeRunConfig {
|
||||
pub fn prepare(&mut self, api_port: u16) -> Result<(), M3uFilterError> {
|
||||
pub fn prepare(&mut self, api_port: u16) -> Result<(), TuliProxError> {
|
||||
let mut names = HashSet::new();
|
||||
let mut ports = HashSet::new();
|
||||
ports.insert(api_port);
|
||||
@@ -80,11 +80,11 @@ impl HdHomeRunConfig {
|
||||
device.prepare(device_num);
|
||||
if names.contains(&device.name) {
|
||||
names.insert(&device.name);
|
||||
return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "HdHomeRun duplicate device name {}", device.name);
|
||||
return create_tuliprox_error_result!(TuliProxErrorKind::Info, "HdHomeRun duplicate device name {}", device.name);
|
||||
}
|
||||
if device.port > 0 && ports.contains(&device.port) {
|
||||
ports.insert(device.port);
|
||||
return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "HdHomeRun duplicate port {}", device.port);
|
||||
return create_tuliprox_error_result!(TuliProxErrorKind::Info, "HdHomeRun duplicate port {}", device.port);
|
||||
}
|
||||
}
|
||||
let mut current_port = api_port + 1;
|
||||
|
||||
+16
-16
@@ -9,8 +9,8 @@ use std::sync::atomic::AtomicU32;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::foundation::filter::{apply_templates_to_pattern, get_filter, prepare_templates, Filter, PatternTemplate, RegexWithCaptures, ValueProcessor};
|
||||
use crate::m3u_filter_error::{create_m3u_filter_error_result, handle_m3u_filter_error_result, info_err};
|
||||
use crate::m3u_filter_error::{M3uFilterError, M3uFilterErrorKind};
|
||||
use crate::tuliprox_error::{create_tuliprox_error_result, handle_tuliprox_error_result, info_err};
|
||||
use crate::tuliprox_error::{TuliProxError, TuliProxErrorKind};
|
||||
use crate::model::valid_property;
|
||||
use crate::model::{ItemField};
|
||||
use crate::model::{FieldGetAccessor, FieldSetAccessor, PlaylistItem};
|
||||
@@ -74,9 +74,9 @@ impl Display for CounterModifier {
|
||||
}
|
||||
|
||||
impl FromStr for CounterModifier {
|
||||
type Err = M3uFilterError;
|
||||
type Err = TuliProxError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, M3uFilterError> {
|
||||
fn from_str(s: &str) -> Result<Self, TuliProxError> {
|
||||
if s.eq("assign") {
|
||||
Ok(Self::Assign)
|
||||
} else if s.eq("suffix") {
|
||||
@@ -84,7 +84,7 @@ impl FromStr for CounterModifier {
|
||||
} else if s.eq("prefix") {
|
||||
Ok(Self::Prefix)
|
||||
} else {
|
||||
create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "Unknown CounterModifier: {}", s)
|
||||
create_tuliprox_error_result!(TuliProxErrorKind::Info, "Unknown CounterModifier: {}", s)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,9 +146,9 @@ impl Display for TransformModifier {
|
||||
}
|
||||
|
||||
impl FromStr for TransformModifier {
|
||||
type Err = M3uFilterError;
|
||||
type Err = TuliProxError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, M3uFilterError> {
|
||||
fn from_str(s: &str) -> Result<Self, TuliProxError> {
|
||||
if s.eq("lowercase") {
|
||||
Ok(Self::Lowercase)
|
||||
} else if s.eq("uppercase") {
|
||||
@@ -156,7 +156,7 @@ impl FromStr for TransformModifier {
|
||||
} else if s.eq("capitalize") {
|
||||
Ok(Self::Capitalize)
|
||||
} else {
|
||||
create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "Unknown TransformModifier: {}", s)
|
||||
create_tuliprox_error_result!(TuliProxErrorKind::Info, "Unknown TransformModifier: {}", s)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -171,7 +171,7 @@ pub struct MapperTransform {
|
||||
}
|
||||
|
||||
impl MapperTransform {
|
||||
pub fn prepare(&mut self, templates: Option<&Vec<PatternTemplate>>) -> Result<(), M3uFilterError> {
|
||||
pub fn prepare(&mut self, templates: Option<&Vec<PatternTemplate>>) -> Result<(), TuliProxError> {
|
||||
match &self.pattern {
|
||||
None => self.t_pattern = None,
|
||||
Some(pattern) => {
|
||||
@@ -184,7 +184,7 @@ impl MapperTransform {
|
||||
}
|
||||
match Regex::new(&new_pattern) {
|
||||
Ok(pattern) => self.t_pattern = Some(pattern),
|
||||
Err(err) => return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "cant parse regex: {new_pattern} {err}"),
|
||||
Err(err) => return create_tuliprox_error_result!(TuliProxErrorKind::Info, "cant parse regex: {new_pattern} {err}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -218,7 +218,7 @@ impl Mapper {
|
||||
/// # Panics
|
||||
///
|
||||
/// Will panic if default `RegEx` gets invalid
|
||||
pub fn prepare(&mut self, templates: Option<&Vec<PatternTemplate>>, tags: Option<&Vec<MappingTag>>) -> Result<(), M3uFilterError> {
|
||||
pub fn prepare(&mut self, templates: Option<&Vec<PatternTemplate>>, tags: Option<&Vec<MappingTag>>) -> Result<(), TuliProxError> {
|
||||
for key in self.attributes.keys() {
|
||||
if !valid_property!(key.as_str(), MAPPER_ATTRIBUTE_FIELDS) {
|
||||
return Err(info_err!(format!("Invalid mapper attribute field {key}")));
|
||||
@@ -453,9 +453,9 @@ pub struct Mapping {
|
||||
|
||||
impl Mapping {
|
||||
pub fn prepare(&mut self, templates: Option<&Vec<PatternTemplate>>,
|
||||
tags: Option<&Vec<MappingTag>>) -> Result<(), M3uFilterError> {
|
||||
tags: Option<&Vec<MappingTag>>) -> Result<(), TuliProxError> {
|
||||
for mapper in &mut self.mapper {
|
||||
handle_m3u_filter_error_result!(M3uFilterErrorKind::Info, mapper.prepare(templates, tags));
|
||||
handle_tuliprox_error_result!(TuliProxErrorKind::Info, mapper.prepare(templates, tags));
|
||||
}
|
||||
|
||||
if let Some(counter_def_list) = &self.counter {
|
||||
@@ -493,7 +493,7 @@ pub struct MappingDefinition {
|
||||
}
|
||||
|
||||
impl MappingDefinition {
|
||||
pub fn prepare(&mut self) -> Result<(), M3uFilterError> {
|
||||
pub fn prepare(&mut self) -> Result<(), TuliProxError> {
|
||||
if let Some(templates) = &mut self.templates {
|
||||
match prepare_templates(templates) {
|
||||
Ok(tmplts) => {
|
||||
@@ -505,7 +505,7 @@ impl MappingDefinition {
|
||||
for mapping in &mut self.mapping {
|
||||
let template_list = self.templates.as_ref();
|
||||
let tag_list = self.tags.as_ref();
|
||||
handle_m3u_filter_error_result!(M3uFilterErrorKind::Info, mapping.prepare(template_list, tag_list));
|
||||
handle_tuliprox_error_result!(TuliProxErrorKind::Info, mapping.prepare(template_list, tag_list));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -517,7 +517,7 @@ pub struct Mappings {
|
||||
}
|
||||
|
||||
impl Mappings {
|
||||
pub fn prepare(&mut self) -> Result<(), M3uFilterError> {
|
||||
pub fn prepare(&mut self) -> Result<(), TuliProxError> {
|
||||
self.mappings.prepare()
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ macro_rules! include_modules {
|
||||
extern crate pest_derive;
|
||||
pub mod api;
|
||||
pub mod auth;
|
||||
pub mod m3u_filter_error;
|
||||
pub mod tuliprox_error;
|
||||
pub mod messaging;
|
||||
pub mod model;
|
||||
pub mod processing;
|
||||
|
||||
@@ -450,7 +450,7 @@ mod tests {
|
||||
/// ```
|
||||
/// parse_normalize().unwrap();
|
||||
/// ```
|
||||
fn parse_normalize() -> Result<(), M3uFilterError> {
|
||||
fn parse_normalize() -> Result<(), TuliProxError> {
|
||||
let epg_normalize = EpgSmartMatchConfig::new()?;
|
||||
let normalized = normalize_channel_name("Love Nature", &epg_normalize);
|
||||
assert_eq!(normalized, "lovenature".to_string());
|
||||
@@ -500,7 +500,7 @@ mod tests {
|
||||
assert_eq!("odisea.bg", normalize_channel_name("BG | ODISEA ᵁᴴᴰ ³⁸⁴⁰ᴾ", &epg_smart_cfg));
|
||||
}
|
||||
|
||||
use crate::m3u_filter_error::M3uFilterError;
|
||||
use crate::tuliprox_error::TuliProxError;
|
||||
use rphonetic::{Encoder, Metaphone};
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::m3u_filter_error::{create_m3u_filter_error_result, M3uFilterError, M3uFilterErrorKind};
|
||||
use crate::tuliprox_error::{create_tuliprox_error_result, TuliProxError, TuliProxErrorKind};
|
||||
use crate::model::ConfigInput;
|
||||
use crate::model::{PlaylistGroup, PlaylistItem, PlaylistItemHeader, PlaylistItemType, XtreamCluster};
|
||||
use crate::model::{XtreamCategory, XtreamSeriesInfo, XtreamSeriesInfoEpisode, XtreamStream};
|
||||
@@ -8,20 +8,20 @@ use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
fn map_to_xtream_category(categories: &Value) -> Result<Vec<XtreamCategory>, M3uFilterError> {
|
||||
fn map_to_xtream_category(categories: &Value) -> Result<Vec<XtreamCategory>, TuliProxError> {
|
||||
match serde_json::from_value::<Vec<XtreamCategory>>(categories.to_owned()) {
|
||||
Ok(xtream_categories) => Ok(xtream_categories),
|
||||
Err(err) => {
|
||||
create_m3u_filter_error_result!(M3uFilterErrorKind::Notify, "Failed to process categories {}", &err)
|
||||
create_tuliprox_error_result!(TuliProxErrorKind::Notify, "Failed to process categories {}", &err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn map_to_xtream_streams(xtream_cluster: XtreamCluster, streams: &Value) -> Result<Vec<XtreamStream>, M3uFilterError> {
|
||||
fn map_to_xtream_streams(xtream_cluster: XtreamCluster, streams: &Value) -> Result<Vec<XtreamStream>, TuliProxError> {
|
||||
match serde_json::from_value::<Vec<XtreamStream>>(streams.to_owned()) {
|
||||
Ok(stream_list) => Ok(stream_list),
|
||||
Err(err) => {
|
||||
create_m3u_filter_error_result!(M3uFilterErrorKind::Notify, "Failed to map to xtream streams {:?}: {}", xtream_cluster, &err)
|
||||
create_tuliprox_error_result!(TuliProxErrorKind::Notify, "Failed to map to xtream streams {:?}: {}", xtream_cluster, &err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,7 @@ fn create_xtream_series_episode_url(url: &str, username: &str, password: &str, e
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_xtream_series_info(info: &Value, group_title: &str, series_name: &str, input: &ConfigInput) -> Result<Option<Vec<(XtreamSeriesInfoEpisode, PlaylistItem)>>, M3uFilterError> {
|
||||
pub fn parse_xtream_series_info(info: &Value, group_title: &str, series_name: &str, input: &ConfigInput) -> Result<Option<Vec<(XtreamSeriesInfoEpisode, PlaylistItem)>>, TuliProxError> {
|
||||
let url = input.url.as_str();
|
||||
let username = input.username.as_ref().map_or("", |v| v);
|
||||
let password = input.password.as_ref().map_or("", |v| v);
|
||||
@@ -70,7 +70,7 @@ pub fn parse_xtream_series_info(info: &Value, group_title: &str, series_name: &s
|
||||
Ok(None)
|
||||
}
|
||||
Err(err) => {
|
||||
create_m3u_filter_error_result!(M3uFilterErrorKind::Notify, "Failed to process series info for {series_name} {err}")
|
||||
create_tuliprox_error_result!(TuliProxErrorKind::Notify, "Failed to process series info for {series_name} {err}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,7 +110,7 @@ pub fn create_xtream_url(xtream_cluster: XtreamCluster, url: &str, username: &st
|
||||
pub fn parse_xtream(input: &ConfigInput,
|
||||
xtream_cluster: XtreamCluster,
|
||||
categories: &Value,
|
||||
streams: &Value) -> Result<Option<Vec<PlaylistGroup>>, M3uFilterError> {
|
||||
streams: &Value) -> Result<Option<Vec<PlaylistGroup>>, TuliProxError> {
|
||||
match map_to_xtream_category(categories) {
|
||||
Ok(xtream_categories) => {
|
||||
let input_name = Arc::new(input.name.to_string());
|
||||
|
||||
@@ -10,7 +10,7 @@ use std::thread;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use crate::foundation::filter::{get_field_value, set_field_value, MockValueProcessor, ValueProvider};
|
||||
use crate::m3u_filter_error::{get_errors_notify_message, notify_err, M3uFilterError, M3uFilterErrorKind};
|
||||
use crate::tuliprox_error::{get_errors_notify_message, notify_err, TuliProxError, TuliProxErrorKind};
|
||||
use crate::messaging::{send_message, MsgKind};
|
||||
use crate::model::{ConfigTarget, InputType, ItemField, ProcessTargets, ProcessingOrder};
|
||||
use crate::model::{CounterModifier, Mapping, MappingValueProcessor};
|
||||
@@ -245,7 +245,7 @@ fn is_target_enabled(target: &ConfigTarget, user_targets: &ProcessTargets) -> bo
|
||||
(!user_targets.enabled && target.enabled) || (user_targets.enabled && user_targets.has_target(target.id))
|
||||
}
|
||||
|
||||
async fn process_source(client: Arc<reqwest::Client>, cfg: Arc<Config>, source_idx: usize, user_targets: Arc<ProcessTargets>) -> (Vec<InputStats>, Vec<TargetStats>, Vec<M3uFilterError>) {
|
||||
async fn process_source(client: Arc<reqwest::Client>, cfg: Arc<Config>, source_idx: usize, user_targets: Arc<ProcessTargets>) -> (Vec<InputStats>, Vec<TargetStats>, Vec<TuliProxError>) {
|
||||
let source = cfg.sources.get(source_idx).unwrap();
|
||||
let mut errors = vec![];
|
||||
let mut input_stats = HashMap::<String, InputStats>::new();
|
||||
@@ -330,14 +330,14 @@ fn create_input_stat(group_count: usize, channel_count: usize, error_count: usiz
|
||||
}
|
||||
}
|
||||
|
||||
async fn process_sources(client: Arc<reqwest::Client>, config: Arc<Config>, user_targets: Arc<ProcessTargets>) -> (Vec<SourceStats>, Vec<M3uFilterError>) {
|
||||
async fn process_sources(client: Arc<reqwest::Client>, config: Arc<Config>, user_targets: Arc<ProcessTargets>) -> (Vec<SourceStats>, Vec<TuliProxError>) {
|
||||
let mut handle_list = vec![];
|
||||
let thread_num = config.threads;
|
||||
let process_parallel = thread_num > 1 && config.sources.len() > 1;
|
||||
if process_parallel && log_enabled!(Level::Debug) {
|
||||
debug!("Using {thread_num} threads");
|
||||
}
|
||||
let errors = Arc::new(Mutex::<Vec<M3uFilterError>>::new(vec![]));
|
||||
let errors = Arc::new(Mutex::<Vec<TuliProxError>>::new(vec![]));
|
||||
let stats = Arc::new(Mutex::<Vec<SourceStats>>::new(vec![]));
|
||||
for (index, _) in config.sources.iter().enumerate() {
|
||||
// We're using the file lock this way on purpose
|
||||
@@ -447,7 +447,7 @@ async fn process_playlist_for_target(client: Arc<reqwest::Client>,
|
||||
target: &ConfigTarget,
|
||||
cfg: &Config,
|
||||
stats: &mut HashMap<String, InputStats>,
|
||||
errors: &mut Vec<M3uFilterError>) -> Result<(), Vec<M3uFilterError>> {
|
||||
errors: &mut Vec<TuliProxError>) -> Result<(), Vec<TuliProxError>> {
|
||||
let pipe = get_processing_pipe(target);
|
||||
debug_if_enabled!("Processing order is {}", &target.processing_order);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::m3u_filter_error::{info_err, notify_err};
|
||||
use crate::m3u_filter_error::{str_to_io_error, to_io_error, M3uFilterError, M3uFilterErrorKind};
|
||||
use crate::tuliprox_error::{info_err, notify_err};
|
||||
use crate::tuliprox_error::{str_to_io_error, to_io_error, TuliProxError, TuliProxErrorKind};
|
||||
use crate::model::{Config, ConfigInput};
|
||||
use crate::model::{FetchedPlaylist, PlaylistEntry, PlaylistItem, PlaylistItemType, XtreamCluster};
|
||||
use crate::repository::storage::get_input_storage_path;
|
||||
@@ -15,7 +15,7 @@ use crate::repository::xtream_repository::xtream_get_record_file_path;
|
||||
use crate::utils::file_utils::append_or_crate_file;
|
||||
use crate::utils::xtream;
|
||||
|
||||
pub(in crate::processing) async fn playlist_resolve_download_playlist_item(client: Arc<reqwest::Client>, pli: &PlaylistItem, input: &ConfigInput, errors: &mut Vec<M3uFilterError>, resolve_delay: u16, cluster: XtreamCluster) -> Option<String> {
|
||||
pub(in crate::processing) async fn playlist_resolve_download_playlist_item(client: Arc<reqwest::Client>, pli: &PlaylistItem, input: &ConfigInput, errors: &mut Vec<TuliProxError>, resolve_delay: u16, cluster: XtreamCluster) -> Option<String> {
|
||||
let mut result = None;
|
||||
let provider_id = pli.get_provider_id()?;
|
||||
if let Some(info_url) = xtream::get_xtream_player_api_info_url(input, cluster, provider_id) {
|
||||
@@ -83,7 +83,7 @@ pub(in crate::processing) fn should_update_info(pli: &mut PlaylistItem, processe
|
||||
|| *old_timestamp.unwrap() != last_modified.unwrap(), provider_id, last_modified.unwrap_or(0))
|
||||
}
|
||||
|
||||
pub(in crate::processing) async fn read_processed_info_ids<V, F>(cfg: &Config, errors: &mut Vec<M3uFilterError>, fpl: &FetchedPlaylist<'_>,
|
||||
pub(in crate::processing) async fn read_processed_info_ids<V, F>(cfg: &Config, errors: &mut Vec<TuliProxError>, fpl: &FetchedPlaylist<'_>,
|
||||
item_type: PlaylistItemType, extract_ts: F) -> HashMap<u32, u64>
|
||||
where
|
||||
F: Fn(&V) -> u64,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::m3u_filter_error::{M3uFilterError, M3uFilterErrorKind};
|
||||
use crate::tuliprox_error::{TuliProxError, TuliProxErrorKind};
|
||||
use crate::model::{Config, ConfigTarget, InputType};
|
||||
use crate::model::{FetchedPlaylist, PlaylistGroup, PlaylistItem, PlaylistItemType, XtreamCluster};
|
||||
use crate::processing::processor::playlist::ProcessingPipe;
|
||||
@@ -7,7 +7,7 @@ use crate::processing::processor::xtream::{create_resolve_episode_wal_files, cre
|
||||
use crate::repository::storage::get_input_storage_path;
|
||||
use crate::repository::xtream_repository::{xtream_get_info_file_paths, xtream_update_input_info_file, xtream_update_input_series_episodes_record_from_wal_file, xtream_update_input_series_record_from_wal_file};
|
||||
use crate::repository::IndexedDocumentReader;
|
||||
use crate::m3u_filter_error::{notify_err, info_err};
|
||||
use crate::tuliprox_error::{notify_err, info_err};
|
||||
use crate::processing::processor::{handle_error, handle_error_and_return, create_resolve_options_function_for_xtream_target};
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
@@ -21,7 +21,7 @@ use crate::utils::file_utils::file_writer;
|
||||
|
||||
create_resolve_options_function_for_xtream_target!(series);
|
||||
|
||||
async fn read_processed_series_info_ids(cfg: &Config, errors: &mut Vec<M3uFilterError>, fpl: &FetchedPlaylist<'_>) -> HashMap<u32, u64> {
|
||||
async fn read_processed_series_info_ids(cfg: &Config, errors: &mut Vec<TuliProxError>, fpl: &FetchedPlaylist<'_>) -> HashMap<u32, u64> {
|
||||
read_processed_info_ids(cfg, errors, fpl, PlaylistItemType::SeriesInfo, |ts: &u64| *ts).await
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ fn should_update_series_info(pli: &mut PlaylistItem, processed_provider_ids: &Ha
|
||||
should_update_info(pli, processed_provider_ids, crate::model::XC_TAG_SERIES_INFO_LAST_MODIFIED)
|
||||
}
|
||||
|
||||
async fn playlist_resolve_series_info(client: Arc<reqwest::Client>, cfg: &Config, errors: &mut Vec<M3uFilterError>,
|
||||
async fn playlist_resolve_series_info(client: Arc<reqwest::Client>, cfg: &Config, errors: &mut Vec<TuliProxError>,
|
||||
fpl: &mut FetchedPlaylist<'_>, resolve_delay: u16) -> bool {
|
||||
let mut processed_info_ids = read_processed_series_info_ids(cfg, errors, fpl).await;
|
||||
// we cant write to the indexed-document directly because of the write lock and time-consuming operation.
|
||||
@@ -132,7 +132,7 @@ async fn playlist_resolve_series_info(client: Arc<reqwest::Client>, cfg: &Config
|
||||
async fn process_series_info(
|
||||
cfg: &Config,
|
||||
fpl: &mut FetchedPlaylist<'_>,
|
||||
errors: &mut Vec<M3uFilterError>,
|
||||
errors: &mut Vec<TuliProxError>,
|
||||
) -> Vec<PlaylistGroup> {
|
||||
let mut result: Vec<PlaylistGroup> = vec![];
|
||||
let input = fpl.input;
|
||||
@@ -214,7 +214,7 @@ async fn process_series_info(
|
||||
|
||||
|
||||
pub async fn playlist_resolve_series(client: Arc<reqwest::Client>, cfg: &Config, target: &ConfigTarget,
|
||||
errors: &mut Vec<M3uFilterError>,
|
||||
errors: &mut Vec<TuliProxError>,
|
||||
pipe: &ProcessingPipe,
|
||||
provider_fpl: &mut FetchedPlaylist<'_>,
|
||||
processed_fpl: &mut FetchedPlaylist<'_>,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::m3u_filter_error::{M3uFilterError, M3uFilterErrorKind};
|
||||
use crate::tuliprox_error::{TuliProxError, TuliProxErrorKind};
|
||||
use crate::model::{Config, ConfigTarget, InputType};
|
||||
use crate::model::{FetchedPlaylist, PlaylistItem, PlaylistItemType, XtreamCluster};
|
||||
use crate::processing::processor::xtream::{create_resolve_info_wal_files, playlist_resolve_download_playlist_item, read_processed_info_ids, should_update_info, write_info_content_to_wal_file};
|
||||
use crate::repository::xtream_repository::{xtream_update_input_info_file, xtream_update_input_vod_record_from_wal_file, InputVodInfoRecord};
|
||||
use crate::m3u_filter_error::{notify_err};
|
||||
use crate::tuliprox_error::{notify_err};
|
||||
use crate::processing::processor::{handle_error, handle_error_and_return, create_resolve_options_function_for_xtream_target};
|
||||
use crate::utils::{get_u32_from_serde_value, get_u64_from_serde_value};
|
||||
use serde_json::{Map, Value};
|
||||
@@ -17,7 +17,7 @@ use crate::utils::file_utils::file_writer;
|
||||
|
||||
create_resolve_options_function_for_xtream_target!(vod);
|
||||
|
||||
async fn read_processed_vod_info_ids(cfg: &Config, errors: &mut Vec<M3uFilterError>, fpl: &FetchedPlaylist<'_>) -> HashMap<u32, u64> {
|
||||
async fn read_processed_vod_info_ids(cfg: &Config, errors: &mut Vec<TuliProxError>, fpl: &FetchedPlaylist<'_>) -> HashMap<u32, u64> {
|
||||
read_processed_info_ids(cfg, errors, fpl, PlaylistItemType::Video, |record: &InputVodInfoRecord| record.ts).await
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ fn should_update_vod_info(pli: &mut PlaylistItem, processed_provider_ids: &HashM
|
||||
should_update_info(pli, processed_provider_ids, crate::model::XC_TAG_VOD_INFO_ADDED)
|
||||
}
|
||||
|
||||
pub async fn playlist_resolve_vod(client: Arc<reqwest::Client>, cfg: &Config, target: &ConfigTarget, errors: &mut Vec<M3uFilterError>, fpl: &mut FetchedPlaylist<'_>) {
|
||||
pub async fn playlist_resolve_vod(client: Arc<reqwest::Client>, cfg: &Config, target: &ConfigTarget, errors: &mut Vec<TuliProxError>, fpl: &mut FetchedPlaylist<'_>) {
|
||||
let (resolve_movies, resolve_delay) = get_resolve_vod_options(target, fpl);
|
||||
if !resolve_movies { return; }
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::io::{self, BufReader, Read, Seek, SeekFrom, Write};
|
||||
use std::marker::PhantomData;
|
||||
use std::mem::size_of;
|
||||
use std::path::Path;
|
||||
use crate::m3u_filter_error::{str_to_io_error, to_io_error};
|
||||
use crate::tuliprox_error::{str_to_io_error, to_io_error};
|
||||
use crate::utils::file_utils::{file_reader, file_writer, open_read_write_file, rename_or_copy};
|
||||
use log::error;
|
||||
use ruzstd::decoding::StreamingDecoder;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::m3u_filter_error::{notify_err, M3uFilterError, M3uFilterErrorKind};
|
||||
use crate::tuliprox_error::{notify_err, TuliProxError, TuliProxErrorKind};
|
||||
use crate::model::{Config, ConfigTarget, TargetOutput};
|
||||
use crate::model::Epg;
|
||||
use crate::repository::m3u_repository::m3u_get_epg_file_path;
|
||||
@@ -9,7 +9,7 @@ use std::fs::File;
|
||||
use std::io::{Cursor, Write};
|
||||
use std::path::Path;
|
||||
|
||||
fn epg_write_file(target: &ConfigTarget, epg: &Epg, path: &Path) -> Result<(), M3uFilterError> {
|
||||
fn epg_write_file(target: &ConfigTarget, epg: &Epg, path: &Path) -> Result<(), TuliProxError> {
|
||||
let mut writer = Writer::new(Cursor::new(vec![]));
|
||||
match epg.write_to(&mut writer) {
|
||||
Ok(()) => {
|
||||
@@ -35,7 +35,7 @@ fn epg_write_file(target: &ConfigTarget, epg: &Epg, path: &Path) -> Result<(), M
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn epg_write(target: &ConfigTarget, cfg: &Config, target_path: &Path, epg: Option<&Epg>, output: &TargetOutput) -> Result<(), M3uFilterError> {
|
||||
pub fn epg_write(target: &ConfigTarget, cfg: &Config, target_path: &Path, epg: Option<&Epg>, output: &TargetOutput) -> Result<(), TuliProxError> {
|
||||
if let Some(epg_data) = epg {
|
||||
match output {
|
||||
TargetOutput::Xtream(_) => {
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::utils::file_utils;
|
||||
use log::error;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tempfile::NamedTempFile;
|
||||
use crate::m3u_filter_error::{str_to_io_error, to_io_error};
|
||||
use crate::tuliprox_error::{str_to_io_error, to_io_error};
|
||||
use crate::utils::{bincode_deserialize, bincode_serialize};
|
||||
use crate::utils::file_utils::{create_new_file_for_read_write, file_reader, file_writer, open_read_write_file, open_readonly_file, rename_or_copy};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::m3u_filter_error::{create_m3u_filter_error_result, info_err};
|
||||
use crate::m3u_filter_error::{M3uFilterError, M3uFilterErrorKind};
|
||||
use crate::tuliprox_error::{create_tuliprox_error_result, info_err};
|
||||
use crate::tuliprox_error::{TuliProxError, TuliProxErrorKind};
|
||||
use crate::model::{ApiProxyServerInfo, ProxyUserCredentials};
|
||||
use crate::model::{ClusterFlags, Config, ConfigTarget, StrmTargetOutput};
|
||||
use crate::model::{
|
||||
@@ -404,12 +404,12 @@ fn extract_item_info(pli: &mut PlaylistItem) -> StrmItemInfo {
|
||||
}
|
||||
}
|
||||
|
||||
async fn prepare_strm_output_directory(path: &Path) -> Result<(), M3uFilterError> {
|
||||
async fn prepare_strm_output_directory(path: &Path) -> Result<(), TuliProxError> {
|
||||
// Ensure the directory exists
|
||||
if let Err(e) = tokio::fs::create_dir_all(path).await {
|
||||
error!("Failed to create directory {path:?}: {e}");
|
||||
return create_m3u_filter_error_result!(
|
||||
M3uFilterErrorKind::Notify,
|
||||
return create_tuliprox_error_result!(
|
||||
TuliProxErrorKind::Notify,
|
||||
"Error creating STRM directory: {e}"
|
||||
);
|
||||
}
|
||||
@@ -575,7 +575,7 @@ pub async fn kodi_write_strm_playlist(
|
||||
target_output: &StrmTargetOutput,
|
||||
cfg: &Config,
|
||||
new_playlist: &mut [PlaylistGroup],
|
||||
) -> Result<(), M3uFilterError> {
|
||||
) -> Result<(), TuliProxError> {
|
||||
if new_playlist.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::m3u_filter_error::info_err;
|
||||
use crate::m3u_filter_error::{M3uFilterError, M3uFilterErrorKind};
|
||||
use crate::tuliprox_error::info_err;
|
||||
use crate::tuliprox_error::{TuliProxError, TuliProxErrorKind};
|
||||
use crate::model::{ProxyType, ProxyUserCredentials};
|
||||
use crate::model::{Config, ConfigTarget, ConfigTargetOptions, TargetType};
|
||||
use crate::model::{M3uPlaylistItem, PlaylistItemType, XtreamCluster};
|
||||
@@ -31,7 +31,7 @@ impl M3uPlaylistIterator {
|
||||
cfg: &Config,
|
||||
target: &ConfigTarget,
|
||||
user: &ProxyUserCredentials,
|
||||
) -> Result<Self, M3uFilterError> {
|
||||
) -> Result<Self, TuliProxError> {
|
||||
let m3u_output = target.get_m3u_output().ok_or_else(|| info_err!(format!("Unexpected failure, missing m3u target output for target {}", target.name)))?;
|
||||
let target_path = ensure_target_storage_path(cfg, target.name.as_str())?;
|
||||
let (m3u_path, idx_path) = m3u_get_file_paths(&target_path);
|
||||
@@ -138,7 +138,7 @@ impl M3uPlaylistM3uTextIterator {
|
||||
cfg: &Config,
|
||||
target: &ConfigTarget,
|
||||
user: &ProxyUserCredentials,
|
||||
) -> Result<Self, M3uFilterError> {
|
||||
) -> Result<Self, TuliProxError> {
|
||||
Ok(Self {
|
||||
inner: M3uPlaylistIterator::new(cfg, target, user).await?,
|
||||
started: false,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::m3u_filter_error::{create_m3u_filter_error,info_err};
|
||||
use crate::m3u_filter_error::{str_to_io_error, M3uFilterError, M3uFilterErrorKind};
|
||||
use crate::tuliprox_error::{create_tuliprox_error,info_err};
|
||||
use crate::tuliprox_error::{str_to_io_error, TuliProxError, TuliProxErrorKind};
|
||||
use crate::model::ProxyUserCredentials;
|
||||
use crate::model::{Config, ConfigTarget, M3uTargetOutput};
|
||||
use crate::model::{M3uPlaylistItem, PlaylistGroup, PlaylistItem, PlaylistItemType};
|
||||
@@ -18,7 +18,7 @@ use crate::repository::storage_const;
|
||||
|
||||
macro_rules! cant_write_result {
|
||||
($path:expr, $err:expr) => {
|
||||
create_m3u_filter_error!(M3uFilterErrorKind::Notify, "failed to write m3u playlist: {} - {}", $path.to_str().unwrap() ,$err)
|
||||
create_tuliprox_error!(TuliProxErrorKind::Notify, "failed to write m3u playlist: {} - {}", $path.to_str().unwrap() ,$err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ fn persist_m3u_playlist_as_text(cfg: &Config, target: &ConfigTarget, target_outp
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn m3u_write_playlist( cfg: &Config, target: &ConfigTarget, target_output: &M3uTargetOutput, target_path: &Path, new_playlist: &[PlaylistGroup]) -> Result<(), M3uFilterError> {
|
||||
pub async fn m3u_write_playlist( cfg: &Config, target: &ConfigTarget, target_output: &M3uTargetOutput, target_path: &Path, new_playlist: &[PlaylistGroup]) -> Result<(), TuliProxError> {
|
||||
if !new_playlist.is_empty() {
|
||||
let (m3u_path, idx_path) = m3u_get_file_paths(target_path);
|
||||
let m3u_playlist = new_playlist.iter()
|
||||
@@ -85,7 +85,7 @@ pub async fn m3u_load_rewrite_playlist(
|
||||
cfg: &Config,
|
||||
target: &ConfigTarget,
|
||||
user: &ProxyUserCredentials,
|
||||
) -> Result<M3uPlaylistM3uTextIterator, M3uFilterError> {
|
||||
) -> Result<M3uPlaylistM3uTextIterator, TuliProxError> {
|
||||
M3uPlaylistM3uTextIterator::new(cfg, target, user).await
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::m3u_filter_error::info_err;
|
||||
use crate::m3u_filter_error::{M3uFilterError, M3uFilterErrorKind};
|
||||
use crate::tuliprox_error::info_err;
|
||||
use crate::tuliprox_error::{TuliProxError, TuliProxErrorKind};
|
||||
use crate::model::{Config, ConfigTarget, TargetOutput};
|
||||
use crate::model::{PlaylistGroup, PlaylistItemType};
|
||||
use crate::model::Epg;
|
||||
@@ -14,7 +14,7 @@ use crate::utils::request::{is_dash_url, is_hls_url};
|
||||
use std::path::Path;
|
||||
|
||||
pub async fn persist_playlist(playlist: &mut [PlaylistGroup], epg: Option<&Epg>,
|
||||
target: &ConfigTarget, cfg: &Config) -> Result<(), Vec<M3uFilterError>> {
|
||||
target: &ConfigTarget, cfg: &Config) -> Result<(), Vec<TuliProxError>> {
|
||||
let mut errors = vec![];
|
||||
let target_path = match ensure_target_storage_path(cfg, &target.name) {
|
||||
Ok(path) => path,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::fmt::Write;
|
||||
use crate::m3u_filter_error::{M3uFilterError, M3uFilterErrorKind};
|
||||
use crate::tuliprox_error::{TuliProxError, TuliProxErrorKind};
|
||||
use crate::model::{Config};
|
||||
use crate::model::UUIDType;
|
||||
use crate::m3u_filter_error::{notify_err};
|
||||
use crate::tuliprox_error::{notify_err};
|
||||
use crate::repository::storage_const;
|
||||
use crate::utils::file_utils;
|
||||
|
||||
@@ -54,7 +54,7 @@ pub(in crate::repository) fn get_target_id_mapping_file(target_path: &Path) -> P
|
||||
target_path.join(PathBuf::from(storage_const::FILE_ID_MAPPING))
|
||||
}
|
||||
|
||||
pub fn ensure_target_storage_path(cfg: &Config, target_name: &str) -> Result<PathBuf, M3uFilterError> {
|
||||
pub fn ensure_target_storage_path(cfg: &Config, target_name: &str) -> Result<PathBuf, TuliProxError> {
|
||||
if let Some(path) = get_target_storage_path(cfg, target_name) {
|
||||
if std::fs::create_dir_all(&path).is_err() {
|
||||
let msg = format!("Failed to save target data, can't create directory {path:?}");
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::collections::HashSet;
|
||||
use log::error;
|
||||
use crate::m3u_filter_error::info_err;
|
||||
use crate::m3u_filter_error::{M3uFilterError, M3uFilterErrorKind};
|
||||
use crate::tuliprox_error::info_err;
|
||||
use crate::tuliprox_error::{TuliProxError, TuliProxErrorKind};
|
||||
use crate::model::{ProxyUserCredentials};
|
||||
use crate::model::{Config, ConfigTarget, TargetType};
|
||||
use crate::model::{XtreamCluster, XtreamPlaylistItem};
|
||||
@@ -27,7 +27,7 @@ impl XtreamPlaylistIterator {
|
||||
target: &ConfigTarget,
|
||||
category_id: Option<u32>,
|
||||
user: &ProxyUserCredentials,
|
||||
) -> Result<Self, M3uFilterError> {
|
||||
) -> Result<Self, TuliProxError> {
|
||||
let xtream_output = target.get_xtream_output().ok_or_else(|| info_err!(format!("Unexpected: xtream output required for target {}", target.name)))?;
|
||||
if let Some(storage_path) = xtream_get_storage_path(config, target.name.as_str()) {
|
||||
let (xtream_path, idx_path) = xtream_get_file_paths(&storage_path, cluster);
|
||||
@@ -90,7 +90,7 @@ pub async fn new(
|
||||
target: &ConfigTarget,
|
||||
category_id: Option<u32>,
|
||||
user: &ProxyUserCredentials,
|
||||
) -> Result<Self, M3uFilterError> {
|
||||
) -> Result<Self, TuliProxError> {
|
||||
Ok(Self {
|
||||
inner: XtreamPlaylistIterator::new(cluster, config, target, category_id, user).await?
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::m3u_filter_error::{create_m3u_filter_error, create_m3u_filter_error_result, info_err, notify_err, str_to_io_error, M3uFilterError, M3uFilterErrorKind};
|
||||
use crate::tuliprox_error::{create_tuliprox_error, create_tuliprox_error_result, info_err, notify_err, str_to_io_error, TuliProxError, TuliProxErrorKind};
|
||||
use crate::model::{ProxyUserCredentials};
|
||||
use crate::model::{Config, ConfigInput, ConfigTarget, XtreamTargetOutput};
|
||||
use crate::model::{PlaylistEntry, PlaylistGroup, PlaylistItem, PlaylistItemType, XtreamCluster, XtreamPlaylistItem};
|
||||
@@ -31,8 +31,8 @@ use std::sync::Arc;
|
||||
|
||||
macro_rules! cant_write_result {
|
||||
($path:expr, $err:expr) => {
|
||||
create_m3u_filter_error!(
|
||||
M3uFilterErrorKind::Notify,
|
||||
create_tuliprox_error!(
|
||||
TuliProxErrorKind::Notify,
|
||||
"failed to write xtream playlist: {} - {}",
|
||||
$path.to_str().unwrap(),
|
||||
$err
|
||||
@@ -60,7 +60,7 @@ fn get_collection_path(path: &Path, collection: &str) -> PathBuf {
|
||||
path.join(format!("{collection}.json"))
|
||||
}
|
||||
|
||||
fn ensure_xtream_storage_path(cfg: &Config, target_name: &str) -> Result<PathBuf, M3uFilterError> {
|
||||
fn ensure_xtream_storage_path(cfg: &Config, target_name: &str) -> Result<PathBuf, TuliProxError> {
|
||||
if let Some(path) = xtream_get_storage_path(cfg, target_name) {
|
||||
if std::fs::create_dir_all(&path).is_err() {
|
||||
let msg = format!(
|
||||
@@ -104,7 +104,7 @@ fn write_playlists_to_file(
|
||||
cfg: &Config,
|
||||
storage_path: &Path,
|
||||
collections: Vec<(XtreamCluster, &[&mut PlaylistItem])>,
|
||||
) -> Result<(), M3uFilterError> {
|
||||
) -> Result<(), TuliProxError> {
|
||||
for (cluster, playlist) in collections {
|
||||
let (xtream_path, idx_path) = xtream_get_file_paths(storage_path, cluster);
|
||||
{
|
||||
@@ -200,7 +200,7 @@ pub async fn xtream_write_playlist(
|
||||
target: &ConfigTarget,
|
||||
cfg: &Config,
|
||||
playlist: &mut [PlaylistGroup],
|
||||
) -> Result<(), M3uFilterError> {
|
||||
) -> Result<(), TuliProxError> {
|
||||
let path = ensure_xtream_storage_path(cfg, target.name.as_str())?;
|
||||
let mut errors = Vec::new();
|
||||
let mut cat_live_col = Vec::with_capacity(1_000);
|
||||
@@ -280,8 +280,8 @@ pub async fn xtream_write_playlist(
|
||||
}
|
||||
|
||||
if !errors.is_empty() {
|
||||
return create_m3u_filter_error_result!(
|
||||
M3uFilterErrorKind::Notify,
|
||||
return create_tuliprox_error_result!(
|
||||
TuliProxErrorKind::Notify,
|
||||
"{}",
|
||||
errors.join("\n")
|
||||
);
|
||||
@@ -385,7 +385,7 @@ pub async fn xtream_load_rewrite_playlist(
|
||||
target: &ConfigTarget,
|
||||
category_id: Option<u32>,
|
||||
user: &ProxyUserCredentials,
|
||||
) -> Result<XtreamPlaylistJsonIterator, M3uFilterError> {
|
||||
) -> Result<XtreamPlaylistJsonIterator, TuliProxError> {
|
||||
XtreamPlaylistJsonIterator::new(cluster, config, target, category_id, user).await
|
||||
}
|
||||
|
||||
@@ -728,7 +728,7 @@ pub async fn xtream_update_input_info_file(
|
||||
input: &ConfigInput,
|
||||
wal_path: &Path,
|
||||
cluster: XtreamCluster,
|
||||
) -> Result<(), M3uFilterError> {
|
||||
) -> Result<(), TuliProxError> {
|
||||
match get_input_storage_path(&input.name, &cfg.working_dir).map(|storage_path| xtream_get_info_file_paths(&storage_path, cluster)) {
|
||||
Ok(Some((info_path, idx_path))) => {
|
||||
{
|
||||
@@ -771,7 +771,7 @@ pub async fn xtream_update_input_vod_record_from_wal_file(
|
||||
cfg: &Config,
|
||||
input: &ConfigInput,
|
||||
wal_path: &Path,
|
||||
) -> Result<(), M3uFilterError> {
|
||||
) -> Result<(), TuliProxError> {
|
||||
let record_path = get_input_storage_path(&input.name, &cfg.working_dir).map(|storage_path| xtream_get_record_file_path(&storage_path, PlaylistItemType::Video))
|
||||
.map_err(|err| notify_err!(format!("Error accessing storage path: {err}")))
|
||||
.and_then(|opt| opt.ok_or_else(|| notify_err!(format!("Error accessing storage path for input: {}", &input.name))))?;
|
||||
@@ -811,7 +811,7 @@ pub async fn xtream_update_input_series_record_from_wal_file(
|
||||
cfg: &Config,
|
||||
input: &ConfigInput,
|
||||
wal_path: &Path,
|
||||
) -> Result<(), M3uFilterError> {
|
||||
) -> Result<(), TuliProxError> {
|
||||
let record_path = get_input_storage_path(&input.name, &cfg.working_dir).map(|storage_path| xtream_get_record_file_path(&storage_path, PlaylistItemType::SeriesInfo))
|
||||
.map_err(|err| notify_err!(format!("Error accessing storage path: {err}")))
|
||||
.and_then(|opt| opt.ok_or_else(|| notify_err!(format!("Error accessing storage path for input: {}", &input.name))))?;
|
||||
@@ -845,7 +845,7 @@ pub async fn xtream_update_input_series_episodes_record_from_wal_file(
|
||||
cfg: &Config,
|
||||
input: &ConfigInput,
|
||||
wal_path: &Path,
|
||||
) -> Result<(), M3uFilterError> {
|
||||
) -> Result<(), TuliProxError> {
|
||||
let record_path = get_input_storage_path(&input.name, &cfg.working_dir).map(|storage_path| xtream_get_record_file_path(&storage_path, PlaylistItemType::Series))
|
||||
.map_err(|err| notify_err!(format!("Error accessing storage path: {err}")))
|
||||
.and_then(|opt| opt.ok_or_else(|| notify_err!(format!("Error accessing storage path for input: {}", &input.name))))?;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use std::io;
|
||||
use crate::m3u_filter_error::to_io_error;
|
||||
use crate::tuliprox_error::to_io_error;
|
||||
|
||||
#[inline]
|
||||
pub fn bincode_serialize<T>(value: &T) -> io::Result<Vec<u8>>
|
||||
|
||||
+11
-11
@@ -1,4 +1,4 @@
|
||||
use crate::m3u_filter_error::{M3uFilterError, M3uFilterErrorKind};
|
||||
use crate::tuliprox_error::{TuliProxError, TuliProxErrorKind};
|
||||
use base64::{engine::general_purpose, Engine as _};
|
||||
use openssl::symm::{Cipher, Crypter, Mode};
|
||||
use rand::Rng;
|
||||
@@ -32,13 +32,13 @@ pub fn deobfuscate_text(secret: &[u8], text: &str) -> Result<String, String> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encrypt_text(secret: &[u8; 16], text: &str) -> Result<String, M3uFilterError> {
|
||||
pub fn encrypt_text(secret: &[u8; 16], text: &str) -> Result<String, TuliProxError> {
|
||||
let iv: [u8; 16] = rand::rng().random(); // Random IV (AES-CBC 16 Bytes)
|
||||
let cipher = Cipher::aes_128_cbc();
|
||||
let mut crypter = Crypter::new(cipher, Mode::Encrypt, secret, Some(&iv)).map_err(|err| M3uFilterError::new(M3uFilterErrorKind::Info, err.to_string()))?;
|
||||
let mut crypter = Crypter::new(cipher, Mode::Encrypt, secret, Some(&iv)).map_err(|err| TuliProxError::new(TuliProxErrorKind::Info, err.to_string()))?;
|
||||
let mut ciphertext = vec![0; text.len() + cipher.block_size()];
|
||||
let mut count = crypter.update(text.as_bytes(), &mut ciphertext).map_err(|err| M3uFilterError::new(M3uFilterErrorKind::Info, err.to_string()))?;
|
||||
count += crypter.finalize(&mut ciphertext[count..]).map_err(|err| M3uFilterError::new(M3uFilterErrorKind::Info, err.to_string()))?;
|
||||
let mut count = crypter.update(text.as_bytes(), &mut ciphertext).map_err(|err| TuliProxError::new(TuliProxErrorKind::Info, err.to_string()))?;
|
||||
count += crypter.finalize(&mut ciphertext[count..]).map_err(|err| TuliProxError::new(TuliProxErrorKind::Info, err.to_string()))?;
|
||||
ciphertext.truncate(count);
|
||||
|
||||
// IV + Ciphertext
|
||||
@@ -47,17 +47,17 @@ pub fn encrypt_text(secret: &[u8; 16], text: &str) -> Result<String, M3uFilterEr
|
||||
Ok(general_purpose::URL_SAFE_NO_PAD.encode(out))
|
||||
}
|
||||
|
||||
pub fn decrypt_text(secret: &[u8; 16], encrypted_text: &str) -> Result<String, M3uFilterError> {
|
||||
let data = general_purpose::URL_SAFE_NO_PAD.decode(encrypted_text).map_err(|err| M3uFilterError::new(M3uFilterErrorKind::Info, err.to_string()))?;
|
||||
pub fn decrypt_text(secret: &[u8; 16], encrypted_text: &str) -> Result<String, TuliProxError> {
|
||||
let data = general_purpose::URL_SAFE_NO_PAD.decode(encrypted_text).map_err(|err| TuliProxError::new(TuliProxErrorKind::Info, err.to_string()))?;
|
||||
let (iv, ciphertext) = data.split_at(16); // first 16 bytes IV
|
||||
let cipher = Cipher::aes_128_cbc();
|
||||
let mut crypter = Crypter::new(cipher, Mode::Decrypt, secret, Some(iv)).map_err(|err| M3uFilterError::new(M3uFilterErrorKind::Info, err.to_string()))?;
|
||||
let mut crypter = Crypter::new(cipher, Mode::Decrypt, secret, Some(iv)).map_err(|err| TuliProxError::new(TuliProxErrorKind::Info, err.to_string()))?;
|
||||
let mut decrypted = vec![0; ciphertext.len() + cipher.block_size()];
|
||||
let mut count = crypter.update(ciphertext, &mut decrypted).map_err(|err| M3uFilterError::new(M3uFilterErrorKind::Info, err.to_string()))?;
|
||||
count += crypter.finalize(&mut decrypted[count..]).map_err(|err| M3uFilterError::new(M3uFilterErrorKind::Info, err.to_string()))?;
|
||||
let mut count = crypter.update(ciphertext, &mut decrypted).map_err(|err| TuliProxError::new(TuliProxErrorKind::Info, err.to_string()))?;
|
||||
count += crypter.finalize(&mut decrypted[count..]).map_err(|err| TuliProxError::new(TuliProxErrorKind::Info, err.to_string()))?;
|
||||
decrypted.truncate(count);
|
||||
|
||||
String::from_utf8(decrypted).map_err(|err| M3uFilterError::new(M3uFilterErrorKind::Info, err.to_string()))
|
||||
String::from_utf8(decrypted).map_err(|err| TuliProxError::new(TuliProxErrorKind::Info, err.to_string()))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::m3u_filter_error::{create_m3u_filter_error, create_m3u_filter_error_result, handle_m3u_filter_error_result, info_err, str_to_io_error, to_io_error, M3uFilterError, M3uFilterErrorKind};
|
||||
use crate::tuliprox_error::{create_tuliprox_error, create_tuliprox_error_result, handle_tuliprox_error_result, info_err, str_to_io_error, to_io_error, TuliProxError, TuliProxErrorKind};
|
||||
use crate::model::ApiProxyConfig;
|
||||
use crate::model::{Config, ConfigDto, ConfigInput, ConfigInputAlias, InputType};
|
||||
use crate::model::Mappings;
|
||||
@@ -41,7 +41,7 @@ pub fn config_file_reader(file: File, resolve_env: bool) -> impl Read
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_mappings(args_mapping: Option<String>, cfg: &mut Config, resolve_env: bool) -> Result<Option<String>, M3uFilterError> {
|
||||
pub fn read_mappings(args_mapping: Option<String>, cfg: &mut Config, resolve_env: bool) -> Result<Option<String>, TuliProxError> {
|
||||
let mappings_file: String = args_mapping.unwrap_or_else(|| file_utils::get_default_mappings_path(cfg.t_config_path.as_str()));
|
||||
|
||||
match read_mapping(mappings_file.as_str(), resolve_env) {
|
||||
@@ -61,7 +61,7 @@ pub fn read_mappings(args_mapping: Option<String>, cfg: &mut Config, resolve_env
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn read_api_proxy_config(args_api_proxy_config: Option<String>, cfg: &mut Config) -> Result<Option<String>, M3uFilterError> {
|
||||
pub async fn read_api_proxy_config(args_api_proxy_config: Option<String>, cfg: &mut Config) -> Result<Option<String>, TuliProxError> {
|
||||
let api_proxy_config_file: String = args_api_proxy_config.unwrap_or_else(|| file_utils::get_default_api_proxy_config_path(cfg.t_config_path.as_str()));
|
||||
api_proxy_config_file.clone_into(&mut cfg.t_api_proxy_file_path);
|
||||
let api_proxy_config = read_api_proxy(cfg, api_proxy_config_file.as_str(), true);
|
||||
@@ -77,7 +77,7 @@ pub async fn read_api_proxy_config(args_api_proxy_config: Option<String>, cfg: &
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_config(config_path: &str, config_file: &str, sources_file: &str, include_computed: bool) -> Result<Config, M3uFilterError> {
|
||||
pub fn read_config(config_path: &str, config_file: &str, sources_file: &str, include_computed: bool) -> Result<Config, TuliProxError> {
|
||||
let files = vec![std::path::PathBuf::from(config_file), std::path::PathBuf::from(sources_file)];
|
||||
match multi_file_reader::MultiFileReader::new(&files) {
|
||||
Ok(reader) => {
|
||||
@@ -92,21 +92,21 @@ pub fn read_config(config_path: &str, config_file: &str, sources_file: &str, inc
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "cant read config file: {}", e)
|
||||
create_tuliprox_error_result!(TuliProxErrorKind::Info, "cant read config file: {}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "{}", err)
|
||||
Err(err) => create_tuliprox_error_result!(TuliProxErrorKind::Info, "{}", err)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_mapping(mapping_file: &str, resolve_var: bool) -> Result<Option<Mappings>, M3uFilterError> {
|
||||
pub fn read_mapping(mapping_file: &str, resolve_var: bool) -> Result<Option<Mappings>, TuliProxError> {
|
||||
let mapping_file = std::path::PathBuf::from(mapping_file);
|
||||
if let Ok(file) = file_utils::open_file(&mapping_file) {
|
||||
let mapping: Result<Mappings, _> = serde_yaml::from_reader(config_file_reader(file, resolve_var));
|
||||
return match mapping {
|
||||
Ok(mut result) => {
|
||||
handle_m3u_filter_error_result!(M3uFilterErrorKind::Info, result.prepare());
|
||||
handle_tuliprox_error_result!(TuliProxErrorKind::Info, result.prepare());
|
||||
Ok(Some(result))
|
||||
}
|
||||
Err(err) => {
|
||||
@@ -140,7 +140,7 @@ pub fn read_api_proxy(config: &Config, api_proxy_file: &str, resolve_env: bool)
|
||||
})
|
||||
}
|
||||
|
||||
fn write_config_file<T>(file_path: &str, backup_dir: &str, config: &T, default_name: &str) -> Result<(), M3uFilterError>
|
||||
fn write_config_file<T>(file_path: &str, backup_dir: &str, config: &T, default_name: &str) -> Result<(), TuliProxError>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
@@ -157,14 +157,14 @@ where
|
||||
|
||||
File::create(&path)
|
||||
.and_then(|f| serde_yaml::to_writer(f, &config).map_err(to_io_error))
|
||||
.map_err(|err| create_m3u_filter_error!(M3uFilterErrorKind::Info, "Could not write file {}: {}", &path.to_str().unwrap_or("?"), err))
|
||||
.map_err(|err| create_tuliprox_error!(TuliProxErrorKind::Info, "Could not write file {}: {}", &path.to_str().unwrap_or("?"), err))
|
||||
}
|
||||
|
||||
pub fn save_api_proxy(file_path: &str, backup_dir: &str, config: &ApiProxyConfig) -> Result<(), M3uFilterError> {
|
||||
pub fn save_api_proxy(file_path: &str, backup_dir: &str, config: &ApiProxyConfig) -> Result<(), TuliProxError> {
|
||||
write_config_file(file_path, backup_dir, config, "api-proxy.yml")
|
||||
}
|
||||
|
||||
pub fn save_main_config(file_path: &str, backup_dir: &str, config: &ConfigDto) -> Result<(), M3uFilterError> {
|
||||
pub fn save_main_config(file_path: &str, backup_dir: &str, config: &ConfigDto) -> Result<(), TuliProxError> {
|
||||
write_config_file(file_path, backup_dir, config, "config.yml")
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::sync::Arc;
|
||||
use std::{fmt, io};
|
||||
use std::path::{Path, PathBuf};
|
||||
use tokio::sync::{Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
use crate::m3u_filter_error::str_to_io_error;
|
||||
use crate::tuliprox_error::str_to_io_error;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FileLockManager {
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::fs::{File, OpenOptions};
|
||||
use std::io::{BufReader, BufWriter, Read, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::m3u_filter_error::str_to_io_error;
|
||||
use crate::tuliprox_error::str_to_io_error;
|
||||
use crate::utils::debug_if_enabled;
|
||||
use log::{debug, error};
|
||||
use path_clean::PathClean;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::m3u_filter_error::M3uFilterError;
|
||||
use crate::tuliprox_error::TuliProxError;
|
||||
use crate::model::{Config, ConfigInput};
|
||||
use crate::model::TVGuide;
|
||||
use crate::repository::storage::short_hash;
|
||||
@@ -10,7 +10,7 @@ use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
||||
async fn download_epg_file(url: &str, client: &Arc<reqwest::Client>, input: &ConfigInput, working_dir: &str) -> Result<PathBuf, M3uFilterError> {
|
||||
async fn download_epg_file(url: &str, client: &Arc<reqwest::Client>, input: &ConfigInput, working_dir: &str) -> Result<PathBuf, TuliProxError> {
|
||||
debug!("Getting epg file path for url: {url}");
|
||||
let file_prefix = short_hash(url);
|
||||
let persist_file_path = prepare_file_path(input.persist.as_deref(), working_dir, "")
|
||||
@@ -19,7 +19,7 @@ async fn download_epg_file(url: &str, client: &Arc<reqwest::Client>, input: &Con
|
||||
request::get_input_text_content_as_file(Arc::clone(client), input, working_dir, url, persist_file_path).await
|
||||
}
|
||||
|
||||
pub async fn get_xmltv(client: Arc<reqwest::Client>, _cfg: &Config, input: &ConfigInput, working_dir: &str) -> (Option<TVGuide>, Vec<M3uFilterError>) {
|
||||
pub async fn get_xmltv(client: Arc<reqwest::Client>, _cfg: &Config, input: &ConfigInput, working_dir: &str) -> (Option<TVGuide>, Vec<TuliProxError>) {
|
||||
match &input.epg {
|
||||
None => (None, vec![]),
|
||||
Some(epg_config) => {
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
use crate::m3u_filter_error::{M3uFilterError, M3uFilterErrorKind};
|
||||
use crate::tuliprox_error::{TuliProxError, TuliProxErrorKind};
|
||||
use crate::model::IpCheckConfig;
|
||||
use regex::Regex;
|
||||
use reqwest::Client;
|
||||
use std::sync::Arc;
|
||||
|
||||
async fn fetch_ip(client: &Arc<Client>, url: &str, regex: Option<&Regex>) -> Result<String, M3uFilterError> {
|
||||
async fn fetch_ip(client: &Arc<Client>, url: &str, regex: Option<&Regex>) -> Result<String, TuliProxError> {
|
||||
let response = client.get(url).send().await
|
||||
.map_err(|e| M3uFilterError::new(M3uFilterErrorKind::Info, format!("Failed to request {url}: {e}")))?;
|
||||
.map_err(|e| TuliProxError::new(TuliProxErrorKind::Info, format!("Failed to request {url}: {e}")))?;
|
||||
|
||||
let text = response.text().await
|
||||
.map_err(|e| M3uFilterError::new(M3uFilterErrorKind::Info, format!("Failed to read response: {e}")))?;
|
||||
.map_err(|e| TuliProxError::new(TuliProxErrorKind::Info, format!("Failed to read response: {e}")))?;
|
||||
|
||||
if let Some(re) = regex {
|
||||
return if let Some(caps) = re.captures(&text) {
|
||||
if let Some(m) = caps.get(1) {
|
||||
Ok(m.as_str().to_string())
|
||||
} else {
|
||||
Err(M3uFilterError::new(M3uFilterErrorKind::Info, "Regex matched but no group found".to_string()))
|
||||
Err(TuliProxError::new(TuliProxErrorKind::Info, "Regex matched but no group found".to_string()))
|
||||
}
|
||||
} else {
|
||||
Err(M3uFilterError::new(M3uFilterErrorKind::Info, "Regex did not match".to_string()))
|
||||
Err(TuliProxError::new(TuliProxErrorKind::Info, "Regex did not match".to_string()))
|
||||
};
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ async fn fetch_combined_ips(client: &Arc<Client>, config: &IpCheckConfig, url: &
|
||||
}
|
||||
|
||||
/// Fetch both IPv4 and IPv6 addresses, using separate or combined URL(s)
|
||||
pub async fn get_ips(client: &Arc<Client>, config: &IpCheckConfig) -> Result<(Option<String>, Option<String>), M3uFilterError> {
|
||||
pub async fn get_ips(client: &Arc<Client>, config: &IpCheckConfig) -> Result<(Option<String>, Option<String>), TuliProxError> {
|
||||
match (&config.url_ipv4, &config.url_ipv6, &config.url) {
|
||||
// Both dedicated URLs provided
|
||||
(Some(url_v4), Some(url_v6), _) => {
|
||||
@@ -82,6 +82,6 @@ pub async fn get_ips(client: &Arc<Client>, config: &IpCheckConfig) -> Result<(Op
|
||||
}
|
||||
|
||||
// No URLs given
|
||||
_ => Err(M3uFilterError::new(M3uFilterErrorKind::Info, "No valid IP-check URLs provided".to_owned())),
|
||||
_ => Err(TuliProxError::new(TuliProxErrorKind::Info, "No valid IP-check URLs provided".to_owned())),
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
use std::sync::Arc;
|
||||
use crate::m3u_filter_error::M3uFilterError;
|
||||
use crate::tuliprox_error::TuliProxError;
|
||||
use crate::model::{Config, ConfigInput};
|
||||
use crate::model::PlaylistGroup;
|
||||
use crate::processing::parser::m3u;
|
||||
use crate::utils::file_utils::prepare_file_path;
|
||||
use crate::utils::request;
|
||||
|
||||
pub async fn get_m3u_playlist(client: Arc<reqwest::Client>, cfg: &Config, input: &ConfigInput, working_dir: &str) -> (Vec<PlaylistGroup>, Vec<M3uFilterError>) {
|
||||
pub async fn get_m3u_playlist(client: Arc<reqwest::Client>, cfg: &Config, input: &ConfigInput, working_dir: &str) -> (Vec<PlaylistGroup>, Vec<TuliProxError>) {
|
||||
let url = input.url.clone();
|
||||
let persist_file_path = prepare_file_path(input.persist.as_deref(), working_dir, "");
|
||||
match request::get_input_text_content(client, input, working_dir, &url, persist_file_path).await {
|
||||
|
||||
@@ -14,8 +14,8 @@ use reqwest::header::CONTENT_ENCODING;
|
||||
use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
|
||||
use url::Url;
|
||||
|
||||
use crate::m3u_filter_error::create_m3u_filter_error_result;
|
||||
use crate::m3u_filter_error::{str_to_io_error, M3uFilterError, M3uFilterErrorKind};
|
||||
use crate::tuliprox_error::create_tuliprox_error_result;
|
||||
use crate::tuliprox_error::{str_to_io_error, TuliProxError, TuliProxErrorKind};
|
||||
use crate::model::format_elapsed_time;
|
||||
use crate::model::{ConfigInput, ProxyConfig, InputFetchMethod};
|
||||
use crate::repository::storage::{get_input_storage_path, short_hash};
|
||||
@@ -29,14 +29,14 @@ pub const fn bytes_to_megabytes(bytes: u64) -> u64 {
|
||||
bytes / 1_048_576
|
||||
}
|
||||
|
||||
pub async fn get_input_text_content_as_file(client: Arc<reqwest::Client>, input: &ConfigInput, working_dir: &str, url_str: &str, persist_filepath: Option<PathBuf>) -> Result<PathBuf, M3uFilterError> {
|
||||
pub async fn get_input_text_content_as_file(client: Arc<reqwest::Client>, input: &ConfigInput, working_dir: &str, url_str: &str, persist_filepath: Option<PathBuf>) -> Result<PathBuf, TuliProxError> {
|
||||
debug_if_enabled!("getting input text content working_dir: {}, url: {}", working_dir, sanitize_sensitive_info(url_str));
|
||||
if url_str.parse::<url::Url>().is_ok() {
|
||||
match download_text_content_as_file(client, input, url_str, working_dir, persist_filepath).await {
|
||||
Ok(content) => Ok(content),
|
||||
Err(e) => {
|
||||
error!("cant download input url: {} => {}", sanitize_sensitive_info(url_str), sanitize_sensitive_info(e.to_string().as_str()));
|
||||
create_m3u_filter_error_result!(M3uFilterErrorKind::Notify, "Failed to download")
|
||||
create_tuliprox_error_result!(TuliProxErrorKind::Notify, "Failed to download")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -49,7 +49,7 @@ pub async fn get_input_text_content_as_file(client: Arc<reqwest::Client>, input:
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
error!("cant persist to: {} => {}", to_file.to_str().unwrap_or("?"), e);
|
||||
return create_m3u_filter_error_result!(M3uFilterErrorKind::Notify, "Failed to persist: {} => {}", to_file.to_str().unwrap_or("?"), e);
|
||||
return create_tuliprox_error_result!(TuliProxErrorKind::Notify, "Failed to persist: {} => {}", to_file.to_str().unwrap_or("?"), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,7 @@ pub async fn get_input_text_content_as_file(client: Arc<reqwest::Client>, input:
|
||||
if filepath.exists() {
|
||||
Some(filepath)
|
||||
} else {
|
||||
return create_m3u_filter_error_result!(M3uFilterErrorKind::Notify, "Failed: file does not exists {filepath:?}");
|
||||
return create_tuliprox_error_result!(TuliProxErrorKind::Notify, "Failed: file does not exists {filepath:?}");
|
||||
}
|
||||
} else {
|
||||
None
|
||||
@@ -69,13 +69,13 @@ pub async fn get_input_text_content_as_file(client: Arc<reqwest::Client>, input:
|
||||
result.map_or_else(|| {
|
||||
let msg = format!("cant read input url: {}", sanitize_sensitive_info(url_str));
|
||||
error!("{msg}");
|
||||
create_m3u_filter_error_result!(M3uFilterErrorKind::Notify, "{msg}")
|
||||
create_tuliprox_error_result!(TuliProxErrorKind::Notify, "{msg}")
|
||||
}, Ok)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub async fn get_input_text_content(client: Arc<reqwest::Client>, input: &ConfigInput, working_dir: &str, url_str: &str, persist_filepath: Option<PathBuf>) -> Result<String, M3uFilterError> {
|
||||
pub async fn get_input_text_content(client: Arc<reqwest::Client>, input: &ConfigInput, working_dir: &str, url_str: &str, persist_filepath: Option<PathBuf>) -> Result<String, TuliProxError> {
|
||||
debug_if_enabled!("getting input text content working_dir: {}, url: {}", working_dir, sanitize_sensitive_info(url_str));
|
||||
|
||||
if url_str.parse::<url::Url>().is_ok() {
|
||||
@@ -83,7 +83,7 @@ pub async fn get_input_text_content(client: Arc<reqwest::Client>, input: &Config
|
||||
Ok((content, _response_url)) => Ok(content),
|
||||
Err(e) => {
|
||||
error!("cant download input url: {} => {}", sanitize_sensitive_info(url_str), sanitize_sensitive_info(e.to_string().as_str()));
|
||||
create_m3u_filter_error_result!(M3uFilterErrorKind::Notify, "Failed to download")
|
||||
create_tuliprox_error_result!(TuliProxErrorKind::Notify, "Failed to download")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -96,7 +96,7 @@ pub async fn get_input_text_content(client: Arc<reqwest::Client>, input: &Config
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
error!("cant persist to: {} => {}", to_file.to_str().unwrap_or("?"), e);
|
||||
return create_m3u_filter_error_result!(M3uFilterErrorKind::Notify, "Failed to persist: {} => {}", to_file.to_str().unwrap_or("?"), e);
|
||||
return create_tuliprox_error_result!(TuliProxErrorKind::Notify, "Failed to persist: {} => {}", to_file.to_str().unwrap_or("?"), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,7 +104,7 @@ pub async fn get_input_text_content(client: Arc<reqwest::Client>, input: &Config
|
||||
match get_local_file_content(&filepath) {
|
||||
Ok(content) => Some(content),
|
||||
Err(err) => {
|
||||
return create_m3u_filter_error_result!(M3uFilterErrorKind::Notify, "Failed : {}", err);
|
||||
return create_tuliprox_error_result!(TuliProxErrorKind::Notify, "Failed : {}", err);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -116,7 +116,7 @@ pub async fn get_input_text_content(client: Arc<reqwest::Client>, input: &Config
|
||||
result.map_or_else(|| {
|
||||
let msg = format!("cant read input url: {}", sanitize_sensitive_info(url_str));
|
||||
error!("{msg}");
|
||||
create_m3u_filter_error_result!(M3uFilterErrorKind::Notify, "{msg}")
|
||||
create_tuliprox_error_result!(TuliProxErrorKind::Notify, "{msg}")
|
||||
}, Ok)
|
||||
}
|
||||
}
|
||||
@@ -360,10 +360,10 @@ async fn download_json_content(client: Arc<reqwest::Client>, input: &ConfigInput
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_input_json_content(client: Arc<reqwest::Client>, input: &ConfigInput, url: &str, persist_filepath: Option<PathBuf>) -> Result<serde_json::Value, M3uFilterError> {
|
||||
pub async fn get_input_json_content(client: Arc<reqwest::Client>, input: &ConfigInput, url: &str, persist_filepath: Option<PathBuf>) -> Result<serde_json::Value, TuliProxError> {
|
||||
match download_json_content(client, input, url, persist_filepath).await {
|
||||
Ok(content) => Ok(content),
|
||||
Err(e) => create_m3u_filter_error_result!(M3uFilterErrorKind::Notify, "cant download input url: {} => {}", sanitize_sensitive_info(url), sanitize_sensitive_info(e.to_string().as_str()))
|
||||
Err(e) => create_tuliprox_error_result!(TuliProxErrorKind::Notify, "cant download input url: {} => {}", sanitize_sensitive_info(url), sanitize_sensitive_info(e.to_string().as_str()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use std::sync::Arc;
|
||||
use crate::m3u_filter_error::{str_to_io_error, M3uFilterError};
|
||||
use crate::tuliprox_error::{str_to_io_error, TuliProxError};
|
||||
use crate::model::{Config, ConfigInput, ConfigTarget};
|
||||
use crate::model::{PlaylistEntry, PlaylistGroup, XtreamCluster, XtreamPlaylistItem};
|
||||
use crate::processing::parser::xtream;
|
||||
@@ -129,7 +129,7 @@ const ACTIONS: [(XtreamCluster, &str, &str); 3] = [
|
||||
(XtreamCluster::Video, crate::model::XC_ACTION_GET_VOD_CATEGORIES, crate::model::XC_ACTION_GET_VOD_STREAMS),
|
||||
(XtreamCluster::Series, crate::model::XC_ACTION_GET_SERIES_CATEGORIES, crate::model::XC_ACTION_GET_SERIES)];
|
||||
|
||||
pub async fn get_xtream_playlist(client: Arc<reqwest::Client>, input: &ConfigInput, working_dir: &str) -> (Vec<PlaylistGroup>, Vec<M3uFilterError>) {
|
||||
pub async fn get_xtream_playlist(client: Arc<reqwest::Client>, input: &ConfigInput, working_dir: &str) -> (Vec<PlaylistGroup>, Vec<TuliProxError>) {
|
||||
|
||||
let username = input.username.as_ref().map_or("", |v| v);
|
||||
let password = input.password.as_ref().map_or("", |v| v);
|
||||
|
||||
Reference in New Issue
Block a user