attempt merge
This commit is contained in:
parent
e7a47b751b
commit
b55380d705
2
LICENSE
2
LICENSE
@ -1,7 +1,7 @@
|
|||||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||||
Version 2, December 2004
|
Version 2, December 2004
|
||||||
|
|
||||||
Copyright (C) 2020 Sam Zick <Sam@deathbybandaid.net>
|
Copyright (C) 2017 Sam Zick <Sam@deathbybandaid.net>
|
||||||
|
|
||||||
Everyone is permitted to copy and distribute verbatim or modified
|
Everyone is permitted to copy and distribute verbatim or modified
|
||||||
copies of this license document, and changing it is allowed as long
|
copies of this license document, and changing it is allowed as long
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
<p align="center">fHDHR_TEMPLATE <img src="docs/images/logo.ico" alt="Logo"/></p>
|
<p align="center">fHDHR_Locast <img src="docs/images/logo.ico" alt="Logo"/></p>
|
||||||
|
|
||||||
|
|
||||||
Welcome to the world of streaming content as a DVR device! We use some fancy python here to achieve a system of:
|
Welcome to the world of streaming content as a DVR device! We use some fancy python here to achieve a system of:
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
[main]
|
[main]
|
||||||
# uuid =
|
# uuid =
|
||||||
# cache_dir =
|
# cache_dir =
|
||||||
# servicename = TEMPLATE
|
# servicename = Locast
|
||||||
# reponame = fHDHR_TEMPLATE
|
# reponame = fHDHR_Locast
|
||||||
|
|
||||||
[fhdhr]
|
[fhdhr]
|
||||||
# address = 0.0.0.0
|
# address = 0.0.0.0
|
||||||
@ -10,8 +10,8 @@
|
|||||||
# port = 5004
|
# port = 5004
|
||||||
# stream_type = direct
|
# stream_type = direct
|
||||||
# tuner_count = 4
|
# tuner_count = 4
|
||||||
# friendlyname = fHDHR-TEMPLATE
|
# friendlyname = fHDHR-Locast
|
||||||
# reporting_firmware_name = fHDHR_TEMPLATE
|
# reporting_firmware_name = fHDHR_Locast
|
||||||
# reporting_manufacturer = BoronDust
|
# reporting_manufacturer = BoronDust
|
||||||
# reporting_model = fHDHR
|
# reporting_model = fHDHR
|
||||||
# reporting_firmware_ver = 20201001
|
# reporting_firmware_ver = 20201001
|
||||||
@ -24,7 +24,7 @@
|
|||||||
# update_frequency = 43200
|
# update_frequency = 43200
|
||||||
|
|
||||||
[ffmpeg]
|
[ffmpeg]
|
||||||
# path = ffmpeg
|
# ffmpeg_path = ffmpeg
|
||||||
# bytes_per_read = 1152000
|
# bytes_per_read = 1152000
|
||||||
|
|
||||||
[vlc]
|
[vlc]
|
||||||
@ -41,5 +41,24 @@
|
|||||||
# type = sqlite
|
# type = sqlite
|
||||||
# driver = None
|
# driver = None
|
||||||
|
|
||||||
[template]
|
[locast]
|
||||||
|
# username =
|
||||||
|
# password =
|
||||||
|
# override_zipcode = None
|
||||||
|
# override_latitude =
|
||||||
|
# override_longitude =
|
||||||
# force_best = False
|
# force_best = False
|
||||||
|
|
||||||
|
[zap2it]
|
||||||
|
# delay = 5
|
||||||
|
# postalcode = None
|
||||||
|
# affiliate_id = gapzap
|
||||||
|
# country = USA
|
||||||
|
# device = -
|
||||||
|
# headendid = lineupId
|
||||||
|
# isoverride = True
|
||||||
|
# languagecode = en
|
||||||
|
# pref =
|
||||||
|
# timespan = 6
|
||||||
|
# timezone =
|
||||||
|
# userid = -
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
|
[locast]
|
||||||
|
# username =
|
||||||
|
# password =
|
||||||
|
|
||||||
[fhdhr]
|
[fhdhr]
|
||||||
# address = 0.0.0.0
|
# address = 0.0.0.0
|
||||||
# port = 5004
|
# port = 5004
|
||||||
|
|||||||
39
config.ini
Normal file
39
config.ini
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
[main]
|
||||||
|
uuid = dymunyqo
|
||||||
|
|
||||||
|
[fhdhr]
|
||||||
|
address = 199.254.167.1
|
||||||
|
port = 5006
|
||||||
|
stream_type = ffmpeg
|
||||||
|
tuner_count = 4
|
||||||
|
friendlyname = fHDHR-Ceton
|
||||||
|
reporting_firmware_name = fHDHR_Ceton
|
||||||
|
reporting_manufacturer = BoronDust
|
||||||
|
reporting_model = fHDHR
|
||||||
|
reporting_firmware_ver = 20201001
|
||||||
|
reporting_tuner_type = Antenna
|
||||||
|
device_auth = fHDHR
|
||||||
|
chanscan_on_start = False
|
||||||
|
|
||||||
|
[epg]
|
||||||
|
method = blocks
|
||||||
|
|
||||||
|
[ffmpeg]
|
||||||
|
|
||||||
|
[vlc]
|
||||||
|
|
||||||
|
[direct_stream]
|
||||||
|
|
||||||
|
[logging]
|
||||||
|
level = info
|
||||||
|
|
||||||
|
[database]
|
||||||
|
|
||||||
|
[ceton]
|
||||||
|
ceton_ip = 199.254.167.17
|
||||||
|
|
||||||
|
[zap2it]
|
||||||
|
|
||||||
|
[web_ui]
|
||||||
|
advanced = False
|
||||||
|
|
||||||
@ -1,7 +1,7 @@
|
|||||||
<p align="center">fHDHR <img src="images/logo.ico" alt="Logo"/></p>
|
<p align="center">fHDHR <img src="images/logo.ico" alt="Logo"/></p>
|
||||||
|
|
||||||
---
|
---
|
||||||
[Main](README.md) | [Setup and Usage](Usage.md) | [template](Origin.md) | [Credits/Related Projects](Related-Projects.md)
|
[Main](README.md) | [Setup and Usage](Usage.md) | [Locast](Origin.md) | [Credits/Related Projects](Related-Projects.md)
|
||||||
---
|
---
|
||||||
**f**un
|
**f**un
|
||||||
**H**ome
|
**H**ome
|
||||||
@ -33,7 +33,7 @@ Here's the `main` section.
|
|||||||
The `fhdhr` contains all the configuration options for interfacing between this script and your media platform.
|
The `fhdhr` contains all the configuration options for interfacing between this script and your media platform.
|
||||||
* `address` and `port` are what we will allow the script to listen on. `0.0.0.0` is the default, and will respond to all.
|
* `address` and `port` are what we will allow the script to listen on. `0.0.0.0` is the default, and will respond to all.
|
||||||
* `discovery_address` may be helpful for making SSDP work properly. If `address` is not `0.0.0.0`, we will use that. If this is not set to a real IP, we won't run SSDP. SSDP is only really helpful for discovering in Plex/Emby. It's a wasted resource since you can manually add the `ip:port` of the script to Plex.
|
* `discovery_address` may be helpful for making SSDP work properly. If `address` is not `0.0.0.0`, we will use that. If this is not set to a real IP, we won't run SSDP. SSDP is only really helpful for discovering in Plex/Emby. It's a wasted resource since you can manually add the `ip:port` of the script to Plex.
|
||||||
* `tuner_count` is a limit of devices able to stream from the script.
|
* `tuner_count` is a limit of devices able to stream from the script. The default is 3, as per Locast's documentation. A 4th is possible, but is not reccomended.
|
||||||
* `friendlyname` is to set the name that Plex sees the script as.
|
* `friendlyname` is to set the name that Plex sees the script as.
|
||||||
* `stream_type` can be set to `ffmpeg`, `vlc` or `direct`.
|
* `stream_type` can be set to `ffmpeg`, `vlc` or `direct`.
|
||||||
|
|
||||||
@ -45,8 +45,8 @@ The `fhdhr` contains all the configuration options for interfacing between this
|
|||||||
# port = 5004
|
# port = 5004
|
||||||
# stream_type = direct
|
# stream_type = direct
|
||||||
# tuner_count = 4
|
# tuner_count = 4
|
||||||
# friendlyname = fHDHR-template
|
# friendlyname = fHDHR-Locast
|
||||||
# reporting_firmware_name = fHDHR_TEMPLATE
|
# reporting_firmware_name = fHDHR_Locast
|
||||||
# reporting_manufacturer = BoronDust
|
# reporting_manufacturer = BoronDust
|
||||||
# reporting_model = fHDHR
|
# reporting_model = fHDHR
|
||||||
# reporting_firmware_ver = 20201001
|
# reporting_firmware_ver = 20201001
|
||||||
@ -56,7 +56,7 @@ The `fhdhr` contains all the configuration options for interfacing between this
|
|||||||
|
|
||||||
# EPG
|
# EPG
|
||||||
* `images` can be set to `proxy` or `pass`. If you choose `proxy`, images will be reverse proxied through fHDHR.
|
* `images` can be set to `proxy` or `pass`. If you choose `proxy`, images will be reverse proxied through fHDHR.
|
||||||
* `method` defaults to `origin` and will pull the xmltv data from template. Other Options include `blocks` which is an hourly schedule with minimal channel information. Another option is `zap2it`, which is another source of EPG information. Channel Numbers may need to be manually mapped.
|
* `method` defaults to `origin` and will pull the xmltv data from Locast. Other Options include `blocks` which is an hourly schedule with minimal channel information. Another option is `zap2it`, which is another source of EPG information. Channel Numbers may need to be manually mapped.
|
||||||
* `update_frequency` * `epg_update_frequency` determines how often we check for new scheduling information. In Seconds.
|
* `update_frequency` * `epg_update_frequency` determines how often we check for new scheduling information. In Seconds.
|
||||||
|
|
||||||
````
|
````
|
||||||
@ -119,11 +119,40 @@ TODO: improve documentation here.
|
|||||||
# driver = None
|
# driver = None
|
||||||
````
|
````
|
||||||
|
|
||||||
## template
|
## Locast
|
||||||
The `template` section
|
The `locast` section
|
||||||
|
* requires `username` and `password`. The script will not run without these.
|
||||||
|
* `override_zipcode` is useful for if your DMA is not being picked up correctly.
|
||||||
|
* `override_latitude` and `override_longitude` are helpful for "mocking" your location.
|
||||||
|
* `force_best` will force select the best stream available.
|
||||||
|
|
||||||
|
|
||||||
````
|
````
|
||||||
[template]
|
[locast]
|
||||||
# username =
|
# username =
|
||||||
# password =
|
# password =
|
||||||
|
# override_zipcode = None
|
||||||
|
# mock_location = None
|
||||||
|
# force_best = False
|
||||||
|
````
|
||||||
|
|
||||||
|
## zap2it
|
||||||
|
|
||||||
|
`zap2it` contains a ton of configuration options, and defaults to options that in my experience don't need to be adjusted.
|
||||||
|
* `postalcode` is a value of importance, and is helpful. If not set, the script will attempt to retrieve your postalcode automatically.
|
||||||
|
|
||||||
|
````
|
||||||
|
[zap2it]
|
||||||
|
# delay = 5
|
||||||
|
# postalcode = None
|
||||||
|
# affiliate_id = gapzap
|
||||||
|
# country = USA
|
||||||
|
# device = -
|
||||||
|
# headendid = lineupId
|
||||||
|
# isoverride = True
|
||||||
|
# languagecode = en
|
||||||
|
# pref =
|
||||||
|
# timespan = 6
|
||||||
|
# timezone =
|
||||||
|
# userid = -
|
||||||
````
|
````
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<p align="center">fHDHR <img src="images/logo.ico" alt="Logo"/></p>
|
<p align="center">fHDHR <img src="images/logo.ico" alt="Logo"/></p>
|
||||||
|
|
||||||
---
|
---
|
||||||
[Main](README.md) | [Setup and Usage](Usage.md) | [template](Origin.md) | [Credits/Related Projects](Related-Projects.md)
|
[Main](README.md) | [Setup and Usage](Usage.md) | [Locast](Origin.md) | [Credits/Related Projects](Related-Projects.md)
|
||||||
---
|
---
|
||||||
**f**un
|
**f**un
|
||||||
**H**ome
|
**H**ome
|
||||||
@ -30,12 +30,12 @@ Under `fhdhr`, you'll find 2 addresses listed. `0.0.0.0` works great for a liste
|
|||||||
# discovery_address = 0.0.0.0
|
# discovery_address = 0.0.0.0
|
||||||
````
|
````
|
||||||
|
|
||||||
## template
|
## Locast
|
||||||
|
|
||||||
template requires signin credentials, so add those.
|
Locast requires signin credentials, so add those.
|
||||||
|
|
||||||
````
|
````
|
||||||
[template]
|
[locast]
|
||||||
# username =
|
# username =
|
||||||
# password =
|
# password =
|
||||||
````
|
````
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<p align="center">fHDHR <img src="images/logo.ico" alt="Logo"/></p>
|
<p align="center">fHDHR <img src="images/logo.ico" alt="Logo"/></p>
|
||||||
|
|
||||||
---
|
---
|
||||||
[Main](README.md) | [Setup and Usage](Usage.md) | [template](Origin.md) | [Credits/Related Projects](Related-Projects.md)
|
[Main](README.md) | [Setup and Usage](Usage.md) | [Locast](Origin.md) | [Credits/Related Projects](Related-Projects.md)
|
||||||
---
|
---
|
||||||
**f**un
|
**f**un
|
||||||
**H**ome
|
**H**ome
|
||||||
@ -12,4 +12,16 @@
|
|||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
This varient of fHDHR connects to [template](https://template.com/about).
|
This varient of fHDHR connects to [Locast](https://www.locast.org/about).
|
||||||
|
|
||||||
|
Usage with fHDHR requires:
|
||||||
|
|
||||||
|
* an account with their service
|
||||||
|
* an active donation
|
||||||
|
* The service to be available in your DMA (“Designated Market Area”)
|
||||||
|
|
||||||
|
Do NOT use fHDHR to obtain channels outside of your DMA. You will recieve no support for doing so.
|
||||||
|
This includes but is not limited to:
|
||||||
|
|
||||||
|
* VPN services
|
||||||
|
* Cloud Server Hosting
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<p align="center">fHDHR <img src="images/logo.ico" alt="Logo"/></p>
|
<p align="center">fHDHR <img src="images/logo.ico" alt="Logo"/></p>
|
||||||
|
|
||||||
---
|
---
|
||||||
[Main](README.md) | [Setup and Usage](Usage.md) | [template](Origin.md) | [Credits/Related Projects](Related-Projects.md)
|
[Main](README.md) | [Setup and Usage](Usage.md) | [Locast](Origin.md) | [Credits/Related Projects](Related-Projects.md)
|
||||||
---
|
---
|
||||||
**f**un
|
**f**un
|
||||||
**H**ome
|
**H**ome
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<p align="center">fHDHR <img src="images/logo.ico" alt="Logo"/></p>
|
<p align="center">fHDHR <img src="images/logo.ico" alt="Logo"/></p>
|
||||||
|
|
||||||
---
|
---
|
||||||
[Main](README.md) | [Setup and Usage](Usage.md) | [template](Origin.md) | [Credits/Related Projects](Related-Projects.md)
|
[Main](README.md) | [Setup and Usage](Usage.md) | [Locast](Origin.md) | [Credits/Related Projects](Related-Projects.md)
|
||||||
---
|
---
|
||||||
**f**un
|
**f**un
|
||||||
**H**ome
|
**H**ome
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<p align="center">fHDHR <img src="images/logo.ico" alt="Logo"/></p>
|
<p align="center">fHDHR <img src="images/logo.ico" alt="Logo"/></p>
|
||||||
|
|
||||||
---
|
---
|
||||||
[Main](README.md) | [Setup and Usage](Usage.md) | [template](Origin.md) | [Credits/Related Projects](Related-Projects.md)
|
[Main](README.md) | [Setup and Usage](Usage.md) | [Locast](Origin.md) | [Credits/Related Projects](Related-Projects.md)
|
||||||
---
|
---
|
||||||
**f**un
|
**f**un
|
||||||
**H**ome
|
**H**ome
|
||||||
@ -50,72 +50,72 @@ fHDHR uses direct connections with video sources by default. Alternatively, you
|
|||||||
## Docker
|
## Docker
|
||||||
This portion of the guide assumes you are using a Linux system with both docker and docker-compose installed. This (or some variation thereof) may work on Mac or Windows, but has not been tested.
|
This portion of the guide assumes you are using a Linux system with both docker and docker-compose installed. This (or some variation thereof) may work on Mac or Windows, but has not been tested.
|
||||||
|
|
||||||
* this guide assumes we wish to use the `~/fhdhr` directory for our install (you can use whatever directory you like, just make the appropriate changes elsewhere in this guide) and that we are installing for template support
|
* this guide assumes we wish to use the `~/fhdhr` directory for our install (you can use whatever directory you like, just make the appropriate changes elsewhere in this guide) and that we are installing for Locast support
|
||||||
* run the following commands to clone the repo into `~/fhdhr/fHDHR_TEMPLATE`
|
* run the following commands to clone the repo into `~/fhdhr/fHDHR_Locast`
|
||||||
```
|
```
|
||||||
cd ~/fhdhr
|
cd ~/fhdhr
|
||||||
git clone https://github.com/fHDHR/fHDHR_TEMPLATE.git
|
git clone https://github.com/fHDHR/fHDHR_Locast.git
|
||||||
```
|
```
|
||||||
* create your config.ini file (as described earlier in this guide) in the `~/fhdhr/fHDHR_TEMPLATE` directory
|
* create your config.ini file (as described earlier in this guide) in the `~/fhdhr/fHDHR_Locast` directory
|
||||||
* while still in the `~/fhdhr` directory, create the following `docker-compose.yml` file
|
* while still in the `~/fhdhr` directory, create the following `docker-compose.yml` file
|
||||||
```
|
```
|
||||||
version: '3'
|
version: '3'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
template:
|
locast:
|
||||||
build: ./fHDHR_TEMPLATE
|
build: ./fHDHR_Locast
|
||||||
container_name: template
|
container_name: locast
|
||||||
network_mode: host
|
network_mode: host
|
||||||
volumes:
|
volumes:
|
||||||
- ./fHDHR_TEMPLATE/config.ini:/app/config/config.ini
|
- ./fHDHR_Locast/config.ini:/app/config/config.ini
|
||||||
```
|
```
|
||||||
* run the following command to build and launch the container
|
* run the following command to build and launch the container
|
||||||
```
|
```
|
||||||
docker-compose up --build -d template
|
docker-compose up --build -d locast
|
||||||
```
|
```
|
||||||
|
|
||||||
After a short period of time (during which docker will build your new fHDHR container), you should now have a working build of fHDHR running inside a docker container.
|
After a short period of time (during which docker will build your new fHDHR container), you should now have a working build of fHDHR running inside a docker container.
|
||||||
|
|
||||||
As the code changes and new versions / bug fixes are released, at any point you can pull the latest version of the code and rebuild your container with the following commands:
|
As the code changes and new versions / bug fixes are released, at any point you can pull the latest version of the code and rebuild your container with the following commands:
|
||||||
```
|
```
|
||||||
cd ~/fhdhr/fHDHR_TEMPLATE
|
cd ~/fhdhr/fHDHR_Locast
|
||||||
git checkout master
|
git checkout master
|
||||||
git pull
|
git pull
|
||||||
cd ~/fhdhr
|
cd ~/fhdhr
|
||||||
docker-compose up --build -d template
|
docker-compose up --build -d locast
|
||||||
```
|
```
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
You can also run multiple instances of fHDHR to support additional sources by cloning the appropriate repo into your `~/fhdhr` directory and adding the necessary services to the docker-compose file we created above.
|
You can also run multiple instances of fHDHR to support additional sources by cloning the appropriate repo into your `~/fhdhr` directory and adding the necessary services to the docker-compose file we created above.
|
||||||
|
|
||||||
* for example, if we also wanted template support, you would clone the template repository:
|
* for example, if we also wanted PlutoTV support, you would clone the PlutoTV repository:
|
||||||
```
|
```
|
||||||
cd ~/fhdhr
|
cd ~/fhdhr
|
||||||
git clone https://github.com/fHDHR/fHDHR_TEMPLATE.git
|
git clone https://github.com/fHDHR/fHDHR_PlutoTV.git
|
||||||
```
|
```
|
||||||
* **NOTE**: if you are running multiple services on the same machine, you must change the port in your config.ini file for each one. For example, if template was using the default port of 5004, template cannot also use that port. You must change the port in your template config.ini file to something else (5005, for example).
|
* **NOTE**: if you are running multiple services on the same machine, you must change the port in your config.ini file for each one. For example, if Locast was using the default port of 5004, PlutoTV cannot also use that port. You must change the port in your PlutoTV config.ini file to something else (5005, for example).
|
||||||
* add template as a service in your `docker-compose.yml` file
|
* add plutotv as a service in your `docker-compose.yml` file
|
||||||
```
|
```
|
||||||
version: '3'
|
version: '3'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
template:
|
locast:
|
||||||
build: ./fHDHR_TEMPLATE
|
build: ./fHDHR_Locast
|
||||||
container_name: template
|
container_name: locast
|
||||||
network_mode: host
|
network_mode: host
|
||||||
volumes:
|
volumes:
|
||||||
- ./fHDHR_TEMPLATE/config.ini:/app/config/config.ini
|
- ./fHDHR_Locast/config.ini:/app/config/config.ini
|
||||||
|
|
||||||
template:
|
plutotv:
|
||||||
build: ./fHDHR_TEMPLATE
|
build: ./fHDHR_PlutoTV
|
||||||
container_name: template
|
container_name: plutotv
|
||||||
network_mode: host
|
network_mode: host
|
||||||
volumes:
|
volumes:
|
||||||
- ./fHDHR_TEMPLATE/config.ini:/app/config/config.ini
|
- ./fHDHR_PlutoTV/config.ini:/app/config/config.ini
|
||||||
```
|
```
|
||||||
* run the following command to build and launch the container
|
* run the following command to build and launch the container
|
||||||
```
|
```
|
||||||
docker-compose up --build -d template
|
docker-compose up --build -d plutotv
|
||||||
```
|
```
|
||||||
|
|
||||||
You can repeat these instructions for as many fHDHR containers as your system resources will allow.
|
You can repeat these instructions for as many fHDHR containers as your system resources will allow.
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<p align="center">fHDHR <img src="images/logo.ico" alt="Logo"/></p>
|
<p align="center">fHDHR <img src="images/logo.ico" alt="Logo"/></p>
|
||||||
|
|
||||||
---
|
---
|
||||||
[Main](README.md) | [Setup and Usage](Usage.md) | [template](Origin.md) | [Credits/Related Projects](Related-Projects.md)
|
[Main](README.md) | [Setup and Usage](Usage.md) | [Locast](Origin.md) | [Credits/Related Projects](Related-Projects.md)
|
||||||
---
|
---
|
||||||
**f**un
|
**f**un
|
||||||
**H**ome
|
**H**ome
|
||||||
@ -27,7 +27,7 @@ Below is the main landing page with basic information.
|
|||||||
|
|
||||||
<img src="screenshots/webui_main.PNG" alt="Main Page"/>
|
<img src="screenshots/webui_main.PNG" alt="Main Page"/>
|
||||||
|
|
||||||
# template
|
# Locast
|
||||||
|
|
||||||
Here you will have access to some basic information about the service we are proxying.
|
Here you will have access to some basic information about the service we are proxying.
|
||||||
|
|
||||||
|
|||||||
@ -4,5 +4,5 @@ from .origin_channels import *
|
|||||||
from .origin_epg import *
|
from .origin_epg import *
|
||||||
from .origin_web import *
|
from .origin_web import *
|
||||||
|
|
||||||
ORIGIN_NAME = "fHDHR_Locast"
|
ORIGIN_NAME = "fHDHR_Ceton"
|
||||||
ORIGIN_VERSION = "v0.5.0-beta"
|
ORIGIN_VERSION = "v0.5.0-beta"
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
|
import xmltodict
|
||||||
|
import base64
|
||||||
|
|
||||||
|
from random import randint
|
||||||
|
|
||||||
|
|
||||||
class OriginChannels():
|
class OriginChannels():
|
||||||
@ -8,10 +12,137 @@ class OriginChannels():
|
|||||||
|
|
||||||
def get_channels(self):
|
def get_channels(self):
|
||||||
|
|
||||||
channel_list = []
|
stations_url = ('http://' + self.fhdhr.config.dict["origin"]["ceton_ip"] + '/view_channel_map.cgi?page=0&xml=1')
|
||||||
|
url_headers = {'accept': 'application/xml;q=0.9, */*;q=0.8'}
|
||||||
|
|
||||||
return channel_list
|
try:
|
||||||
|
stationsReq = self.fhdhr.web.session.get(stations_url, headers=url_headers)
|
||||||
|
stationsReq.raise_for_status()
|
||||||
|
except self.fhdhr.web.exceptions.HTTPError as err:
|
||||||
|
self.fhdhr.logger.error('Error while getting stations: %s' % err)
|
||||||
|
return []
|
||||||
|
|
||||||
|
stationsRes = xmltodict.parse(stationsReq.content)
|
||||||
|
cleaned_channels = []
|
||||||
|
|
||||||
|
for station_item in stationsRes['channels']['channel']:
|
||||||
|
nameTmp = station_item["name"]
|
||||||
|
nameTmp_bytes = nameTmp.encode('ascii')
|
||||||
|
namebytes = base64.b64decode(nameTmp_bytes)
|
||||||
|
name = namebytes.decode('ascii')
|
||||||
|
clean_station_item = {
|
||||||
|
"name": name,
|
||||||
|
"callsign": name,
|
||||||
|
"number": station_item["number"],
|
||||||
|
"eia": station_item["eia"],
|
||||||
|
"id": station_item["sourceid"],
|
||||||
|
}
|
||||||
|
|
||||||
|
cleaned_channels.append(clean_station_item)
|
||||||
|
return cleaned_channels
|
||||||
|
|
||||||
|
def get_ceton_getvar(self, instance, query):
|
||||||
|
getVarUrl = ('http://' + self.fhdhr.config.dict["origin"]["ceton_ip"] + '/get_var?i=' + str(instance) + query)
|
||||||
|
|
||||||
|
try:
|
||||||
|
getVarUrlReq = self.fhdhr.web.session.get(getVarUrl)
|
||||||
|
getVarUrlReq.raise_for_status()
|
||||||
|
except self.fhdhr.web.exceptions.HTTPError as err:
|
||||||
|
self.fhdhr.logger.error('Error while getting tuner variable for: %s - %s' % (query, err))
|
||||||
|
return None
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def get_ceton_tuner_status(self, chandict):
|
||||||
|
found = 0
|
||||||
|
count = int(self.fhdhr.config.dict["fhdhr"]["tuner_count"])
|
||||||
|
for instance in range(count):
|
||||||
|
getVarUrl = ('http://' + self.fhdhr.config.dict["origin"]["ceton_ip"] + '/get_var?i=' + str(instance) + '&s=av&v=TransportState')
|
||||||
|
|
||||||
|
try:
|
||||||
|
getVarUrlReq = self.fhdhr.web.session.get(getVarUrl)
|
||||||
|
getVarUrlReq.raise_for_status()
|
||||||
|
except self.fhdhr.web.exceptions.HTTPError as err:
|
||||||
|
self.fhdhr.logger.error('Error while getting tuner status: %s' % err)
|
||||||
|
return None
|
||||||
|
|
||||||
|
if "STOPPED" in getVarUrlReq.text:
|
||||||
|
self.fhdhr.logger.info('Tuner %s selected' % str(instance))
|
||||||
|
found = 1
|
||||||
|
break
|
||||||
|
|
||||||
|
return found, instance
|
||||||
|
|
||||||
|
def startstop_ceton_tuner(self, instance, startstop):
|
||||||
|
if not startstop:
|
||||||
|
port = 0
|
||||||
|
self.fhdhr.logger.info('Tuner %s to be stopped' % str(instance))
|
||||||
|
else:
|
||||||
|
port = randint(41001, 49999)
|
||||||
|
self.fhdhr.logger.info('Tuner %s to be started' % str(instance))
|
||||||
|
|
||||||
|
StartStopUrl = ('http://%s/stream_request.cgi' % self.fhdhr.config.dict["origin"]["ceton_ip"])
|
||||||
|
|
||||||
|
StartStop_data = {"instance_id": instance,
|
||||||
|
"dest_ip": self.fhdhr.config.dict["fhdhr"]["address"],
|
||||||
|
"dest_port": port,
|
||||||
|
"protocol": 0,
|
||||||
|
"start": startstop}
|
||||||
|
# StartStopUrl_headers = {
|
||||||
|
# 'Content-Type': 'application/json',
|
||||||
|
# 'User-Agent': "curl/7.64.1"}
|
||||||
|
|
||||||
|
try:
|
||||||
|
StartStopUrlReq = self.fhdhr.web.session.post(StartStopUrl, StartStop_data)
|
||||||
|
StartStopUrlReq.raise_for_status()
|
||||||
|
except self.fhdhr.web.exceptions.HTTPError as err:
|
||||||
|
self.fhdhr.logger.error('Error while setting station stream: %s' % err)
|
||||||
|
return None
|
||||||
|
|
||||||
|
return port
|
||||||
|
|
||||||
|
def set_ceton_tuner(self, chandict, instance):
|
||||||
|
tuneChannelUrl = ('http://%s/channel_request.cgi' % self.fhdhr.config.dict["origin"]["ceton_ip"])
|
||||||
|
tuneChannel_data = {"instance_id": 0,
|
||||||
|
"channel": chandict['number']}
|
||||||
|
|
||||||
|
try:
|
||||||
|
tuneChannelUrlReq = self.fhdhr.web.session.post(tuneChannelUrl, tuneChannel_data)
|
||||||
|
tuneChannelUrlReq.raise_for_status()
|
||||||
|
except self.fhdhr.web.exceptions.HTTPError as err:
|
||||||
|
self.fhdhr.logger.error('Error while tuning station URL: %s' % err)
|
||||||
|
return None
|
||||||
|
|
||||||
|
return 1
|
||||||
|
|
||||||
def get_channel_stream(self, chandict):
|
def get_channel_stream(self, chandict):
|
||||||
streamurl = ""
|
# streamurl = "rtsp://admin:password1@199.254.167.199:554"
|
||||||
|
# print (streamurl)
|
||||||
|
# return streamurl
|
||||||
|
|
||||||
|
found, instance = self.get_ceton_tuner_status(chandict)
|
||||||
|
|
||||||
|
# 1 to start or 0 to stop
|
||||||
|
if found:
|
||||||
|
port = self.startstop_ceton_tuner(instance, 1)
|
||||||
|
self.fhdhr.logger.error('No tuners available')
|
||||||
|
else:
|
||||||
|
port = 0
|
||||||
|
|
||||||
|
if port:
|
||||||
|
tuned = self.set_ceton_tuner(chandict, instance)
|
||||||
|
self.fhdhr.logger.error('Preparing tuner ' + str(instance) + ' on port:' + str(port))
|
||||||
|
else:
|
||||||
|
tuned = 0
|
||||||
|
|
||||||
|
self.get_ceton_getvar(instance, "&s=tuner&v=Frequency")
|
||||||
|
self.get_ceton_getvar(instance, "&s=mux&v=ProgramNumber")
|
||||||
|
self.get_ceton_getvar(instance, "&s=diag&v=CopyProtectionStatus")
|
||||||
|
|
||||||
|
if tuned:
|
||||||
|
self.fhdhr.logger.error('Initiate streaming from tuner ' + str(instance))
|
||||||
|
streamurl = "udp://127.0.0.1:" + str(port)
|
||||||
|
else:
|
||||||
|
streamurl = None
|
||||||
|
|
||||||
return streamurl
|
return streamurl
|
||||||
|
|||||||
@ -1,29 +1,39 @@
|
|||||||
{
|
{
|
||||||
"main":{
|
"main":{
|
||||||
"servicename":{
|
"servicename":{
|
||||||
"value": "TEMPLATE",
|
"value": "Ceton",
|
||||||
"config_file": false,
|
"config_file": false,
|
||||||
"config_web": false
|
"config_web": false
|
||||||
},
|
},
|
||||||
"dictpopname":{
|
"dictpopname":{
|
||||||
"value": "template",
|
"value": "ceton",
|
||||||
"config_file": false,
|
"config_file": false,
|
||||||
"config_web": false
|
"config_web": false
|
||||||
},
|
},
|
||||||
"reponame":{
|
"reponame":{
|
||||||
"value": "fHDHR_TEMPLATE",
|
"value": "fHDHR_Ceton",
|
||||||
|
"config_file": false,
|
||||||
|
"config_web": false
|
||||||
|
},
|
||||||
|
"required":{
|
||||||
|
"value": "ceton/ceton_ip",
|
||||||
"config_file": false,
|
"config_file": false,
|
||||||
"config_web": false
|
"config_web": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fhdhr":{
|
"fhdhr":{
|
||||||
"friendlyname":{
|
"friendlyname":{
|
||||||
"value": "fHDHR-TEMPLATE",
|
"value": "fHDHR-Ceton",
|
||||||
"config_file": true,
|
"config_file": true,
|
||||||
"config_web": true
|
"config_web": true
|
||||||
},
|
},
|
||||||
|
"tuner_count":{
|
||||||
|
"value": 3,
|
||||||
|
"config_file": true,
|
||||||
|
"config_web": true
|
||||||
|
},
|
||||||
"reporting_firmware_name":{
|
"reporting_firmware_name":{
|
||||||
"value": "fHDHR_TEMPLATE",
|
"value": "fHDHR_Ceton",
|
||||||
"config_file": true,
|
"config_file": true,
|
||||||
"config_web": true
|
"config_web": true
|
||||||
}
|
}
|
||||||
@ -35,10 +45,16 @@
|
|||||||
"config_web": true
|
"config_web": true
|
||||||
},
|
},
|
||||||
"valid_epg_methods":{
|
"valid_epg_methods":{
|
||||||
"value": "None,blocks,origin",
|
"value": "None,blocks,origin,zap2it,tvtv",
|
||||||
"config_file": false,
|
"config_file": false,
|
||||||
"config_web": false
|
"config_web": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"template":{}
|
"ceton":{
|
||||||
|
"ceton_ip":{
|
||||||
|
"value": "none",
|
||||||
|
"config_file": true,
|
||||||
|
"config_web": true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,10 +8,4 @@ class OriginEPG():
|
|||||||
def update_epg(self, fhdhr_channels):
|
def update_epg(self, fhdhr_channels):
|
||||||
programguide = {}
|
programguide = {}
|
||||||
|
|
||||||
for fhdhr_id in list(fhdhr_channels.list.keys()):
|
|
||||||
chan_obj = fhdhr_channels.list[fhdhr_id]
|
|
||||||
|
|
||||||
if str(chan_obj.dict["number"]) not in list(programguide.keys()):
|
|
||||||
programguide[str(chan_obj.dict["number"])] = chan_obj.epgdict
|
|
||||||
|
|
||||||
return programguide
|
return programguide
|
||||||
|
|||||||
187
origin/origin_rtp.py
Normal file
187
origin/origin_rtp.py
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
import socket
|
||||||
|
# import re
|
||||||
|
import bitstring # if you don't have this from your linux distro, install with "pip install bitstring"
|
||||||
|
|
||||||
|
# ********* (2) The routine for handling the RTP stream ***********
|
||||||
|
|
||||||
|
|
||||||
|
def digestpacket(st):
|
||||||
|
""" This routine takes a UDP packet, i.e. a string of bytes and ..
|
||||||
|
(a) strips off the RTP header
|
||||||
|
(b) adds NAL "stamps" to the packets, so that they are recognized as NAL's
|
||||||
|
(c) Concantenates frames
|
||||||
|
(d) Returns a packet that can be written to disk as such and that is recognized by stock media players as h264 stream
|
||||||
|
"""
|
||||||
|
startbytes = "\x00\x00\x00\x01" # this is the sequence of four bytes that identifies a NAL packet.. must be in front of every NAL packet.
|
||||||
|
|
||||||
|
bt = bitstring.BitArray(bytes=st) # turn the whole string-of-bytes packet into a string of bits. Very unefficient, but hey, this is only for demoing.
|
||||||
|
lc = 12 # bytecounter
|
||||||
|
bc = 12 * 8 # bitcounter
|
||||||
|
|
||||||
|
version = bt[0:2].uint # version
|
||||||
|
p = bt[3] # P
|
||||||
|
x = bt[4] # X
|
||||||
|
cc = bt[4:8].uint # CC
|
||||||
|
m = bt[9] # M
|
||||||
|
pt = bt[9:16].uint # PT
|
||||||
|
sn = bt[16:32].uint # sequence number
|
||||||
|
timestamp = bt[32:64].uint # timestamp
|
||||||
|
ssrc = bt[64:96].uint # ssrc identifier
|
||||||
|
# The header format can be found from:
|
||||||
|
# https://en.wikipedia.org/wiki/Real-time_Transport_Protocol
|
||||||
|
|
||||||
|
lc = 12 # so, we have red twelve bytes
|
||||||
|
bc = 12 * 8 # .. and that many bits
|
||||||
|
|
||||||
|
print("version, p, x, cc, m, pt", version, p, x, cc, m, pt)
|
||||||
|
print("sequence number, timestamp", sn, timestamp)
|
||||||
|
print("sync. source identifier", ssrc)
|
||||||
|
|
||||||
|
# st=f.read(4*cc) # csrc identifiers, 32 bits (4 bytes) each
|
||||||
|
cids = []
|
||||||
|
for i in range(cc):
|
||||||
|
cids.append(bt[bc:bc+32].uint)
|
||||||
|
bc += 32
|
||||||
|
lc += 4
|
||||||
|
print("csrc identifiers:", cids)
|
||||||
|
|
||||||
|
if (x):
|
||||||
|
# this section haven't been tested.. might fail
|
||||||
|
hid = bt[bc:bc+16].uint
|
||||||
|
bc += 16
|
||||||
|
lc += 2
|
||||||
|
|
||||||
|
hlen = bt[bc:bc+16].uint
|
||||||
|
bc += 16
|
||||||
|
lc += 2
|
||||||
|
|
||||||
|
print("ext. header id, header len", hid, hlen)
|
||||||
|
|
||||||
|
# hst = bt[bc:bc+32*hlen]
|
||||||
|
bc += 32 * hlen
|
||||||
|
lc += 4 * hlen
|
||||||
|
|
||||||
|
# OK, now we enter the NAL packet, as described here:
|
||||||
|
#
|
||||||
|
# https://tools.ietf.org/html/rfc6184#section-1.3
|
||||||
|
#
|
||||||
|
# Some quotes from that document:
|
||||||
|
#
|
||||||
|
"""
|
||||||
|
5.3. NAL Unit Header Usage
|
||||||
|
|
||||||
|
|
||||||
|
The structure and semantics of the NAL unit header were introduced in
|
||||||
|
Section 1.3. For convenience, the format of the NAL unit header is
|
||||||
|
reprinted below:
|
||||||
|
|
||||||
|
+---------------+
|
||||||
|
|0|1|2|3|4|5|6|7|
|
||||||
|
+-+-+-+-+-+-+-+-+
|
||||||
|
|F|NRI| Type |
|
||||||
|
+---------------+
|
||||||
|
|
||||||
|
This section specifies the semantics of F and NRI according to this
|
||||||
|
specification.
|
||||||
|
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
Table 3. Summary of allowed NAL unit types for each packetization
|
||||||
|
mode (yes = allowed, no = disallowed, ig = ignore)
|
||||||
|
|
||||||
|
Payload Packet Single NAL Non-Interleaved Interleaved
|
||||||
|
Type Type Unit Mode Mode Mode
|
||||||
|
-------------------------------------------------------------
|
||||||
|
0 reserved ig ig ig
|
||||||
|
1-23 NAL unit yes yes no
|
||||||
|
24 STAP-A no yes no
|
||||||
|
25 STAP-B no no yes
|
||||||
|
26 MTAP16 no no yes
|
||||||
|
27 MTAP24 no no yes
|
||||||
|
28 FU-A no yes yes
|
||||||
|
29 FU-B no no yes
|
||||||
|
30-31 reserved ig ig ig
|
||||||
|
"""
|
||||||
|
# This was also very usefull:
|
||||||
|
# http://stackoverflow.com/questions/7665217/how-to-process-raw-udp-packets-so-that-they-can-be-decoded-by-a-decoder-filter-i
|
||||||
|
# A quote from that:
|
||||||
|
"""
|
||||||
|
First byte: [ 3 NAL UNIT BITS | 5 FRAGMENT TYPE BITS]
|
||||||
|
Second byte: [ START BIT | RESERVED BIT | END BIT | 5 NAL UNIT BITS]
|
||||||
|
Other bytes: [... VIDEO FRAGMENT DATA...]
|
||||||
|
"""
|
||||||
|
|
||||||
|
fb = bt[bc] # i.e. "F"
|
||||||
|
nri = bt[bc+1:bc+3].uint # "NRI"
|
||||||
|
# nlu0 = bt[bc:bc+3] # "3 NAL UNIT BITS" (i.e. [F | NRI])
|
||||||
|
typ = bt[bc+3:bc+8].uint # "Type"
|
||||||
|
print("F, NRI, Type :", fb, nri, typ)
|
||||||
|
print("first three bits together :", bt[bc:bc+3])
|
||||||
|
|
||||||
|
if (typ == 7 or typ == 8):
|
||||||
|
# this means we have either an SPS or a PPS packet
|
||||||
|
# they have the meta-info about resolution, etc.
|
||||||
|
# more reading for example here:
|
||||||
|
# http://www.cardinalpeak.com/blog/the-h-264-sequence-parameter-set/
|
||||||
|
if (typ == 7):
|
||||||
|
print(">>>>> SPS packet")
|
||||||
|
else:
|
||||||
|
print(">>>>> PPS packet")
|
||||||
|
return startbytes+st[lc:]
|
||||||
|
# .. notice here that we include the NAL starting sequence "startbytes" and the "First byte"
|
||||||
|
|
||||||
|
bc += 8
|
||||||
|
lc += 1 # let's go to "Second byte"
|
||||||
|
# ********* WE ARE AT THE "Second byte" ************
|
||||||
|
# The "Type" here is most likely 28, i.e. "FU-A"
|
||||||
|
start = bt[bc] # start bit
|
||||||
|
end = bt[bc+2] # end bit
|
||||||
|
# nlu1 = bt[bc+3:bc+8] # 5 nal unit bits
|
||||||
|
|
||||||
|
if (start): # OK, this is a first fragment in a movie frame
|
||||||
|
print(">>> first fragment found")
|
||||||
|
# nlu = nlu0 + nlu1 # Create "[3 NAL UNIT BITS | 5 NAL UNIT BITS]"
|
||||||
|
# head = startbytes+nlu.bytes # .. add the NAL starting sequence
|
||||||
|
lc += 1 # We skip the "Second byte"
|
||||||
|
if (start is False and end is False): # intermediate fragment in a sequence, just dump "VIDEO FRAGMENT DATA"
|
||||||
|
# head = ""
|
||||||
|
lc += 1 # We skip the "Second byte"
|
||||||
|
elif (end is True): # last fragment in a sequence, just dump "VIDEO FRAGMENT DATA"
|
||||||
|
# head = ""
|
||||||
|
print("<<<< last fragment found")
|
||||||
|
lc += 1 # We skip the "Second byte"
|
||||||
|
|
||||||
|
if (typ == 28): # This code only handles "Type" = 28, i.e. "FU-A"
|
||||||
|
print("missing code here?")
|
||||||
|
else:
|
||||||
|
raise(Exception, "unknown frame type for this piece of s***")
|
||||||
|
|
||||||
|
|
||||||
|
# *********** (3) THE MAIN PROGRAM STARTS HERE ****************
|
||||||
|
|
||||||
|
|
||||||
|
s1 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
|
||||||
|
# unknown name `clientports`
|
||||||
|
# s1.bind(("", clientports[0])) # we open a port that is visible to the whole internet (the empty string "" takes care of that)
|
||||||
|
s1.settimeout(5) # if the socket is dead for 5 s., its thrown into trash
|
||||||
|
# further reading:
|
||||||
|
# https://wiki.python.org/moin/UdpCommunication
|
||||||
|
|
||||||
|
print("** STRIPPING RTP INFO AND DUMPING INTO FILE **")
|
||||||
|
|
||||||
|
# unknown name `fname`
|
||||||
|
# f = open(fname, 'w')
|
||||||
|
|
||||||
|
# unoknown name rn
|
||||||
|
# for i in range(rn):
|
||||||
|
# print
|
||||||
|
# print
|
||||||
|
# recst = s1.recv(4096)
|
||||||
|
# print("read", len(recst), "bytes")
|
||||||
|
# st = digestpacket(recst)
|
||||||
|
# print("dumping", len(st), "bytes")
|
||||||
|
# f.write(st)
|
||||||
|
# f.close()
|
||||||
|
|
||||||
|
s1.close()
|
||||||
@ -1,3 +1,5 @@
|
|||||||
|
# import os
|
||||||
|
# import bitstring
|
||||||
|
|
||||||
|
|
||||||
class OriginService():
|
class OriginService():
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
from .origin_api import Origin_API
|
from .origin_api import Origin_API
|
||||||
|
from .origin_api_tools import Origin_API_Tools
|
||||||
from .origin_html import Origin_HTML
|
from .origin_html import Origin_HTML
|
||||||
|
|
||||||
|
|
||||||
@ -9,4 +10,5 @@ class fHDHR_Origin_Web():
|
|||||||
self.fhdhr = fhdhr
|
self.fhdhr = fhdhr
|
||||||
|
|
||||||
self.origin_api = Origin_API(fhdhr)
|
self.origin_api = Origin_API(fhdhr)
|
||||||
|
self.origin_api_tools = Origin_API_Tools(fhdhr)
|
||||||
self.origin_html = Origin_HTML(fhdhr)
|
self.origin_html = Origin_HTML(fhdhr)
|
||||||
|
|||||||
78
origin/origin_web/origin_api_tools.py
Normal file
78
origin/origin_web/origin_api_tools.py
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
from flask import Response, request, redirect
|
||||||
|
import urllib.parse
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
class Origin_API_Tools():
|
||||||
|
endpoints = ["/api/origin/tools"]
|
||||||
|
endpoint_name = "api_origin_tools"
|
||||||
|
endpoint_methods = ["GET", "POST"]
|
||||||
|
|
||||||
|
def __init__(self, fhdhr):
|
||||||
|
self.fhdhr = fhdhr
|
||||||
|
|
||||||
|
def __call__(self, *args):
|
||||||
|
return self.get(*args)
|
||||||
|
|
||||||
|
def get(self, *args):
|
||||||
|
|
||||||
|
method = request.args.get('method', default="get", type=str)
|
||||||
|
|
||||||
|
redirect_url = request.args.get('redirect', default=None, type=str)
|
||||||
|
|
||||||
|
if method == "channels":
|
||||||
|
|
||||||
|
dma = request.args.get('dma', default=self.fhdhr.originwrapper.originservice.location["DMA"], type=str)
|
||||||
|
stations_url = 'https://api.locastnet.org/api/watch/epg/%s' % dma
|
||||||
|
|
||||||
|
try:
|
||||||
|
stationsReq = self.fhdhr.web.session.get(stations_url)
|
||||||
|
stationsReq.raise_for_status()
|
||||||
|
stationsRes = stationsReq.json()
|
||||||
|
except self.fhdhr.web.exceptions.HTTPError as err:
|
||||||
|
self.fhdhr.logger.error('Error while getting stations: %s' % err)
|
||||||
|
stationsRes = [{"error": 'Error while getting stations: %s' % err, "listings": []}]
|
||||||
|
|
||||||
|
filtered_json = []
|
||||||
|
for station_item in stationsRes:
|
||||||
|
station_item["listings"] = []
|
||||||
|
filtered_json.append(station_item)
|
||||||
|
|
||||||
|
stations_json = json.dumps(filtered_json, indent=4)
|
||||||
|
|
||||||
|
return Response(status=200,
|
||||||
|
response=stations_json,
|
||||||
|
mimetype='application/json')
|
||||||
|
|
||||||
|
elif method == "zipcode":
|
||||||
|
|
||||||
|
zipcode = request.args.get('zipcode', default=None, type=str)
|
||||||
|
if not zipcode:
|
||||||
|
if redirect_url:
|
||||||
|
return redirect(redirect_url + "?retmessage=" + urllib.parse.quote("%s Success" % method))
|
||||||
|
else:
|
||||||
|
return "%s Success" % method
|
||||||
|
|
||||||
|
status_url = 'https://api.locastnet.org/api/watch/dma/zip/%s' % zipcode
|
||||||
|
|
||||||
|
try:
|
||||||
|
statusReq = self.fhdhr.web.session.get(status_url)
|
||||||
|
statusReq.raise_for_status()
|
||||||
|
statusRes = statusReq.json()
|
||||||
|
except self.fhdhr.web.exceptions.HTTPError as err:
|
||||||
|
self.fhdhr.logger.error('Error while getting zipcode status: %s' % err)
|
||||||
|
statusRes = [{"error": 'Error while getting zipcode status: %s' % err, "listings": []}]
|
||||||
|
|
||||||
|
status_json = json.dumps(statusRes, indent=4)
|
||||||
|
|
||||||
|
return Response(status=200,
|
||||||
|
response=status_json,
|
||||||
|
mimetype='application/json')
|
||||||
|
|
||||||
|
else:
|
||||||
|
return "%s Invalid Method" % method
|
||||||
|
|
||||||
|
if redirect_url:
|
||||||
|
return redirect(redirect_url + "?retmessage=" + urllib.parse.quote("%s Success" % method))
|
||||||
|
else:
|
||||||
|
return "%s Success" % method
|
||||||
@ -1,6 +1,9 @@
|
|||||||
from flask import request, render_template_string
|
from flask import request, render_template_string
|
||||||
import pathlib
|
import pathlib
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from fHDHR.tools import humanized_time
|
||||||
|
|
||||||
|
|
||||||
class Origin_HTML():
|
class Origin_HTML():
|
||||||
@ -21,7 +24,6 @@ class Origin_HTML():
|
|||||||
|
|
||||||
if self.fhdhr.originwrapper.setup_success:
|
if self.fhdhr.originwrapper.setup_success:
|
||||||
origin_status_dict = {"Setup": "Success"}
|
origin_status_dict = {"Setup": "Success"}
|
||||||
origin_status_dict["Total Channels"] = len(self.fhdhr.device.channels.list)
|
|
||||||
else:
|
else:
|
||||||
origin_status_dict = {"Setup": "Failed"}
|
origin_status_dict = {"Setup": "Failed"}
|
||||||
return render_template_string(self.template.getvalue(), request=request, fhdhr=self.fhdhr, origin_status_dict=origin_status_dict, list=list)
|
return render_template_string(self.template.getvalue(), request=request, fhdhr=self.fhdhr, origin_status_dict=origin_status_dict, list=list)
|
||||||
|
|||||||
@ -2,7 +2,6 @@ requests
|
|||||||
gevent
|
gevent
|
||||||
flask
|
flask
|
||||||
image
|
image
|
||||||
xmltodict
|
|
||||||
m3u8
|
m3u8
|
||||||
sqlalchemy
|
sqlalchemy
|
||||||
pycryptodome
|
pycryptodome
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user