Introducing dizqueTV

This commit is contained in:
vexorian 2020-08-10 22:53:40 -04:00
parent 71fc2e278a
commit 0bc810ccec
38 changed files with 378 additions and 806 deletions

View File

@ -6,4 +6,5 @@ Dockerfile
.gitignore
bin
dist
.pseudotv
.pseudotv
.dizquetv

1
.gitignore vendored
View File

@ -2,4 +2,5 @@ node_modules/
dist/
bin/
.pseudotv/
.dizquetv/
web/public/bundle.js

112
README.md
View File

@ -1,19 +1,24 @@
# pseudotv-plex
![Discord](https://img.shields.io/discord/711313431457693727?logo=discord&logoColor=fff&style=flat-square) ![GitHub top language](https://img.shields.io/github/languages/top/DEFENDORe/pseudotv?logo=github&style=flat-square) ![Docker Pulls](https://img.shields.io/docker/pulls/defendore/pseudotv?logo=docker&logoColor=fff&style=flat-square)
# dizqueTV
![Discord](https://img.shields.io/discord/711313431457693727?logo=discord&logoColor=fff&style=flat-square) ![GitHub top language](https://img.shields.io/github/languages/top/vexorian/dizquetv?logo=github&style=flat-square) ![Docker Pulls](https://img.shields.io/docker/pulls/vexorian/dizquetv?logo=docker&logoColor=fff&style=flat-square)
Create live TV channel streams from media on your Plex servers.
Project recently migrated from [gitlab](https://gitlab.com/DEFENDORe/pseudotv-plex) to github to improve development flow (docker builds and binary releases).
dizqueTV is a fork of the project previously-known as [pseudotv-plex](https://gitlab.com/DEFENDORe/pseudotv-plex) or [pseudotv](https://github.com/DEFENDORe/pseudotv). New repository because of lack of activity from the main repository and the name change is because projects with the old name already existed and were created long before this approach and it was causing confusion. You can migrate from pseudoTV 0.0.51 to dizqueTV by renaming the .pseudotv folder to .dizquetv and running the new executable (or doing a similar trick with the volumes used by the docker containers).
<img src="./resources/pseudotv.png" width="200">
<img src="./resources/dizquetv.png" width="200">
Configure your channels, programs, commercials and settings using the PseudoTV web UI.
Configure your channels, programs, commercials and settings using the dizqueTV web UI.
Access your channels by adding the spoofed PseudoTV HDHomerun tuner to Plex, or utilize the M3U Url with any 3rd party app.
Access your channels by adding the spoofed dizqueTV HDHomerun tuner to Plex, or utilize the M3U Url with any 3rd party app.
EPG (Guide Information) data is stored to `.pseudotv/xmltv.xml`
EPG (Guide Information) data is stored to `.dizquetv/xmltv.xml`
## Features
- TV Channels are made available as an IPTV stream. There's a lot of IPTV clients and software that supports it.
- Ease of setup for xteve and Plex playback by mocking a HDHR server.
- Centralized server instance so that you need only configure your channels once.
- Customize your TV channels' programming including the specific air times.
- Supports multiple channels all available through a XMLTV tv guide.
- Docker image and prepackage binaries for Windows, Linux and Mac
- Web UI for channel configuration and app settings
- Select media (desired programs and commercials) across multiple Plex servers
@ -21,8 +26,6 @@ EPG (Guide Information) data is stored to `.pseudotv/xmltv.xml`
- Ability to auto update Plex DVR guide data and channel mappings
- Auto update the xmltv.xml file at a set interval (in hours). You can also set the amount EPG cache (in hours).
- Continuous playback support
- Ability to add breaks or padding between episodes and use Commercials, Trailers, Bumpers or other filler.
- Commercial support. 5 commercial slots for a program (BEFORE, 1/4, 1/2, 3/4, AFTER). Place as many commercials as desired per slot to chain commercials.
- Media track selection (video, audio, subtitle). (subtitles disabled by default)
- Subtitle support.
- Ability to overlay channel icon over stream
@ -30,83 +33,96 @@ EPG (Guide Information) data is stored to `.pseudotv/xmltv.xml`
- Can be configured to completely force Direct play.
- Can normalize video formats to prevent stream breaking.
## Limitations
- Plex Pass is required to unlock Plex Live TV/DVR feature
- Only one EPG source can be used with Plex server. This may cause an issue if you are adding the dizquetv tuner to a Plex server with Live TV/DVR already enabled/configured.
* There are projects like xteve that allow you to unify multiple EPG sources into a single list which Plex can use.
- dizqueTV does not currently watch your Plex server for media updates/changes. You must manually remove and readd your programs for any changes to take effect. Same goes for Plex server changes (changing IP, port, etc).. all media will fail..
- Many IPTV players (including Plex) will break after switching episodes if video / audio format is too different between. dizqueTV can be configured to use ffmpeg transcoding to prevent htis, but that costs resources. This is an intrinsic issue with the IPTV approach.
- Plex's IPTV player will be always recording the stream's playback for the purposes of allowing you to pause or rewind the stream. This is not necessarily an issue with other IPTV players.
## Useful Tips/Info
- Internal and External SRT/ASS subtitles may cause a delay when starting stream (only when subtitles are activated). For internal SRT/ASS subtitles, FFMPEG needs to perform a subtitle track extraction from the original media before the requested stream can be started. External SRT/ASS subtitle files still need to be sliced to the correct start time and duration so even they may cause a delay when starting a stream. Image based subs (PGS) should have little to no impact.
- Utilize your hardware accelerated encoders, or use mpeg2 instead of h264 by changing the default video encoder in FFMPEG settings. *Note that some encoders may not be capable of handling every transcoding scenario, libx264 and mpeg2video seem to be the most stable.*
- dizqueTV can use both Plex and ffmpeg transcoding. Plex transcoding is advantageous in that there's access for many more features and formats than would be available.
- Audio track and subtitle choice depends on Plex configuraiton for that video/episode and user.
- Subtitles are transcoded by Plex before being delivered to dizqueTV.
- Can be configured to force a direct stream both from Plex and dizqueTV's side.
- Playing many different kinds of formats and resolutions in the same stream does ntpossible without transcoding them. So unless you are certain that all formats used in the same channel will be identical, your life will be easier if you let ffmpeg be used for normalization.
- If normalization is too heavy, try utilizing your hardware's transcoding features by picking the correct encoder in FFMPEG settings. *Note that some encoders may not be capable of handling every transcoding scenario, libx264 and mpeg2video seem to be the most stable.*
- Intel Quick Sync: `h264_qsv`, `mpeg2_qsv`
- NVIDIA GPU: `h264_nvenc`
- MPEG2 `mpeg2video`
- H264 `libx264` (default)
- MacOS `h264_videotoolbox`
- **Enable the option to log ffmpeg's stderr output directly to the pseudotv app console, for detecting issues**
- Host your own images for channel icons, program icons, etc.. Simply add your image to `.pseudotv/images` and reference them via `http://pseudotv-ip:8000/images/myImage.png`
- **Enable the option to log ffmpeg's stderr output directly to the dizquetv app console, for detecting issues**
- Host your own images for channel icons, program icons, etc.. Simply add your image to `.dizquetv/images` and reference them via `http://dizquetv-ip:8000/images/myImage.png`
- Use the Block Shuffle feature to play a specified number of TV episodes before advancing to the next available TV show in the channel. You can also specify to randomize the TV Show order. Any movies added to the channel will be pushed to the end of the program lineup, this is also applicable the "Sort TV Shows" option.
- Plex is smart enough not to open another stream if it currently is being viewed by another user. This allows only one transcode session for mulitple viewers if they are watching the same channel.
- Even if your Plex server is running on the same machine as the PseudoTV app, use your network address (not a loopback) when configuring your Plex Server(s) in the web UI.
## Limitations
- Plex Pass is required to unlock Plex Live TV/DVR feature
- Only one EPG source can be used with Plex server. This may cause an issue if you are adding the pseudotv tuner to a Plex server with Live TV/DVR already enabled/configured.
* There are projects like xteve that allow you to unify multiple EPG sources into a single list which Plex can use.
- PseudoTV does not watch your Plex server for media updates/changes. You must manually remove and readd your programs for any changes to take effect. Same goes for Plex server changes (changing IP, port, etc).. all media will fail..
- Many IPTV players (including Plex) will break after switching episodes if video / audio format is too different between. PseudoTV can be configured to use ffmpeg transcoding to prevent htis, but that costs resources.
- Use the tools menu in the channel editor to access a lot of features to process your channel's programming, such as shuffling.
- Flex time is a useful feature that allows you to configure breaks between TV shows that play random content. This is useful if you want to simulate "commercials". A frequent use case is to use this filler to pad the starting times of TV shows (so that all TV shows start at :00 or :30 times, for example.
- Even if your Plex server is running on the same machine as the dizqueTV app, use your network address (not a loopback) when configuring your Plex Server(s) in the web UI.
## Installation
*Please delete your old `.pseudotv` directory before using the new build. I'm sorry but it'd take more effort than its worth to convert the old databases..*
* *If you were a pseudotv user, please rename your old `.pseudotv` to `.dizquetv` before running. dizque tv will attempt to migrate your settings and channels to the new features.*
Unless your are using Docker/Unraid, you must download and install **ffmpeg** to your system and set the correct path in the PseudoTV Web UI.
Unless your are using Docker/Unraid, you must download and install **ffmpeg** to your system and set the correct path in the dizqueTV Web UI.
By default, pseudotv will create the directory `.pseudotv` wherever pseudotv is launched from. Your `xmltv.xml` file and config databases are stored here.
By default, dizquetv will create the directory `.dizquetv` wherever dizquetv is launched from. Your `xmltv.xml` file and config databases are stored here.
#### Binary Release
[Download](https://github.com/DEFENDORe/pseudotv/releases) and run the PseudoTV executable (argument defaults below)
[Download](https://github.com/vexorian/dizquetv/releases) and run the dizqueTV executable (argument defaults below)
```
./pseudotv-win-x64.exe --port 8000 --database ./pseudotv
./dizquetv-win-x64.exe --port 8000 --database ./dizquetv
```
#### Docker
The Docker repository can be viewed [here](https://hub.docker.com/r/defendore/pseudotv).
The Docker repository can be viewed [here](https://hub.docker.com/r/vexorian/dizquetv).
Use Docker to fetch PseudoTV, then run the container.. (replace `C:\.pseudotv` with your desired config directory location)
Use Docker to fetch dizqueTV, then run the container.. (replace `C:\.dizquetv` with your desired config directory location)
```
docker pull defendore/pseudotv
docker run --name pseudotv -p 8000:8000 -v C:\.pseudotv:/home/node/app/.pseudotv defendore/pseudotv
docker pull vexorian/dizquetv
docker run --name dizquetv -p 8000:8000 -v C:\.dizquetv:/home/node/app/.dizquetv defendore/dizquetv
```
If you were a pseudotv user, make sure to stop the pseudotv container and use the same folder you used for configuration in pseudotv as configuration for dizquetv.
#### Building Docker image from source
Build docker image from source and run the container. (replace `C:\.pseudotv` with your desired config directory location)
Build docker image from source and run the container. (replace `C:\.dizquetv` with your desired config directory location)
```
git clone https://github.com/DEFENDORe/pseudotv
cd pseudotv-plex
docker build -t pseudotv .
docker run --name pseudotv -p 8000:8000 -v C:\.pseudotv:/home/node/app/.pseudotv pseudotv
git clone https://github.com/vexorian/dizquetv
cd dizquetv
git checkout version
#replace version with the version you want
docker build -t dizquetv .
docker run --name dizquetv -p 8000:8000 -v C:\.dizquetv:/home/node/app/.dizquetv dizquetv
```
#### Unraid Install
Add
```
https://github.com/DEFENDORe/pseudotv
https://github.com/vexorian/dizquetv
```
to your "Template repositories" in the Docker tab.
Click the "Add Container" button
Select either the pseudotv template or the pseudotv-nvidia template if you want nvidia hardware accelerated transcoding.
Make sure you have the Unraid Nvidia plugin installed and change your video encoder to h264_nvenc in the pseudotv ffmpeg settings.
Select either the dizquetv template or the dizquetv-nvidia template if you want nvidia hardware accelerated transcoding.
Make sure you have the Unraid Nvidia plugin installed and change your video encoder to h264_nvenc in the dizquetv ffmpeg settings.
#### From Source
Install NodeJS and FFMPEG
```
git clone https://github.com/DEFENDORe/pseudotv
cd pseudotv-plex
git clone https://github.com/vexorian/dizquetv
cd dizquetv
npm install
npm run build
npm run start
@ -114,21 +130,21 @@ npm run start
## Plex Setup
Add the PseudoTV spoofed HDHomerun tuner to Plex via Plex Settings.
Add the dizqueTV spoofed HDHomerun tuner to Plex via Plex Settings.
If the tuner isn't automatically listed, manually enter the network address of pseudotv. Example:
If the tuner isn't automatically listed, manually enter the network address of dizquetv. Example:
```
127.0.0.1:8000
```
When prompted for a Postal/Zip code, click the `"Have an XMLTV guide on your server? Click here to use that instead."` link.
Enter the location of the `.pseudotv/xmltv.xml` file. Example (Windows):
Enter the location of the `.dizquetv/xmltv.xml` file. Example (Windows):
```
C:\.pseudotv\xmltv.xml
C:\.dizquetv\xmltv.xml
```
**Do not use the Web UI XMLTV URL when feeding Plex the xmltv.xml file. Plex fails to update it's EPG from a URL for some reason (at least on Windows). Use the local file path to `.pseudotv/xmltv.xml`**
**Do not use the Web UI XMLTV URL when feeding Plex the xmltv.xml file. Plex fails to update it's EPG from a URL for some reason (at least on Windows). Use the local file path to `.dizquetv/xmltv.xml`**
## App Preview
<img src="./docs/channels.png" width="500">

View File

@ -15,7 +15,7 @@ const Plex = require('./src/plex');
const channelCache = require('./src/channel-cache');
const constants = require('./src/constants')
console.log("PseudoTV Version: " + constants.VERSION_NAME)
console.log("dizqueTV Version: " + constants.VERSION_NAME)
for (let i = 0, l = process.argv.length; i < l; i++) {
if ((process.argv[i] === "-p" || process.argv[i] === "--port") && i + 1 !== l)
@ -24,11 +24,15 @@ for (let i = 0, l = process.argv.length; i < l; i++) {
process.env.DATABASE = process.argv[i + 1]
}
process.env.DATABASE = process.env.DATABASE || './.pseudotv'
process.env.DATABASE = process.env.DATABASE || './.dizquetv'
process.env.PORT = process.env.PORT || 8000
if (!fs.existsSync(process.env.DATABASE))
if (!fs.existsSync(process.env.DATABASE)) {
if (fs.existsSync("./.pseudotv")) {
throw Error(process.env.DATABASE + " folder not found but ./.pseudotv has been found. Please rename this folder or create an empty " + process.env.DATABASE + " folder so that the program is not confused about.");
}
fs.mkdirSync(process.env.DATABASE)
}
if(!fs.existsSync(path.join(process.env.DATABASE, 'images')))
fs.mkdirSync(path.join(process.env.DATABASE, 'images'))
@ -61,7 +65,7 @@ let xmltvInterval = {
if (plexServers[i].arChannels && channels.length !== 0)
plex.RefreshChannels(channels, dvrs).then(() => { }, (err) => { console.error(err, i) })
}).catch( (err) => {
console.error("There was an error when fetching Plex DVRs. This means pseudoTV couldn't trigger Plex to update its TV guide." + err);
console.error("There was an error when fetching Plex DVRs. This means dizqueTV couldn't trigger Plex to update its TV guide." + err);
});
}
}, (err) => {
@ -107,9 +111,9 @@ function initDB(db) {
let data = fs.readFileSync(path.resolve(path.join(__dirname, 'resources/font.ttf')))
fs.writeFileSync(process.env.DATABASE + '/font.ttf', data)
}
if (!fs.existsSync(process.env.DATABASE + '/images/pseudotv.png')) {
let data = fs.readFileSync(path.resolve(path.join(__dirname, 'resources/pseudotv.png')))
fs.writeFileSync(process.env.DATABASE + '/images/pseudotv.png', data)
if (!fs.existsSync(process.env.DATABASE + '/images/dizquetv.png')) {
let data = fs.readFileSync(path.resolve(path.join(__dirname, 'resources/dizquetv.png')))
fs.writeFileSync(process.env.DATABASE + '/images/dizquetv.png', data)
}
if (!fs.existsSync(process.env.DATABASE + '/images/generic-error-screen.png')) {
let data = fs.readFileSync(path.resolve(path.join(__dirname, 'resources/generic-error-screen.png')))

View File

@ -1,8 +1,8 @@
#!/bin/sh
WIN64=pseudotv-win-x64.exe
WIN32=pseudotv-win-x86.exe
MACOSX=pseudotv-macos-x64
LINUX64=pseudotv-linux-x64
WIN64=dizquetv-win-x64.exe
WIN32=dizquetv-win-x86.exe
MACOSX=dizquetv-macos-x64
LINUX64=dizquetv-linux-x64
rm -R ./dist/*
npm run build

2
package-lock.json generated
View File

@ -1,5 +1,5 @@
{
"name": "pseudotv",
"name": "dizquetv",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,

View File

@ -1,5 +1,5 @@
{
"name": "pseudotv",
"name": "dizquetv",
"version": "1.0.0",
"description": "Create LiveTV channels from your Plex media",
"main": "index.js",
@ -11,7 +11,7 @@
"dev-server": "nodemon index.js --ignore ./web/ --ignore ./db/ --ignore ./xmltv.xml",
"compile": "babel index.js -d dist && babel src -d dist/src",
"package": "sh ./make_dist.sh",
"clean": "del-cli --force ./bin ./dist ./.pseudotv ./web/public/bundle.js"
"clean": "del-cli --force ./bin ./dist ./.dizquetv ./web/public/bundle.js"
},
"author": "Dan Ferguson",
"license": "ISC",

View File

@ -1,30 +1,30 @@
<?xml version="1.0"?>
<Container version="2">
<Name>pseudotv</Name>
<Repository>defendore/pseudotv:latest</Repository>
<Registry>https://hub.docker.com/r/defendore/pseudotv</Registry>
<Name>dizquetv</Name>
<Repository>vexorian/dizque:latest</Repository>
<Registry>https://hub.docker.com/r/vexorian/dizquetv</Registry>
<Network>host</Network>
<MyIP/>
<Shell>bash</Shell>
<Privileged>false</Privileged>
<Support/>
<Project/>
<Overview>PseudoTV is a Plex DVR plugin. It allows you to host your own fake live tv service by dynamically streaming media from your Plex servers(s). Your channels and settings are all manged throught the PseudoTV Web UI.&#xD;
<Overview>dizqueTV is a Plex DVR plugin. It allows you to host your own fake live tv service by dynamically streaming media from your Plex servers(s). Your channels and settings are all managed through the dizqueTV Web UI.&#xD;
&#xD;
PseudoTV will show up as a HDHomeRun device within Plex. When configuring your Plex Tuner, simply use the generatered ./.pseudotv/xmltv.xml file for EPG data. PseudoTV will automatically refresh your Plex server's EPG data and channel mappings (if specified to do so in settings) when configuring channels via the Web UI. Ensure your FFMPEG path is set correctly via the Web UI, and enjoy!</Overview>
dizqueTV will show up as a HDHomeRun device within Plex. When configuring your Plex Tuner, simply use the generatered ./.dizquetv/xmltv.xml file for EPG data. dizqueTV will automatically refresh your Plex server's EPG data and channel mappings (if specified to do so in settings) when configuring channels via the Web UI. Ensure your FFMPEG path is set correctly via the Web UI, and enjoy!</Overview>
<Category/>
<WebUI>http://[IP]:[PORT:8000]</WebUI>
<TemplateURL/>
<Icon>https://raw.githubusercontent.com/DEFENDORe/pseudotv/master/resources/pseudotv.png</Icon>
<Icon>https://raw.githubusercontent.com/vexorian/dizquetv/master/resources/dizquetv.png</Icon>
<ExtraParams>--runtime=nvidia</ExtraParams>
<PostArgs/>
<CPUset/>
<DateInstalled>1589436589</DateInstalled>
<DonateText/>
<DonateLink/>
<Description>PseudoTV is a Plex DVR plugin. It allows you to host your own fake live tv service by dynamically streaming media from your Plex servers(s). Your channels and settings are all manged throught the PseudoTV Web UI.&#xD;
<Description>dizqueTV is a Plex DVR plugin. It allows you to host your own fake live tv service by dynamically streaming media from your Plex servers(s). Your channels and settings are all manged throught the dizqueTV Web UI.&#xD;
&#xD;
PseudoTV will show up as a HDHomeRun device within Plex. When configuring your Plex Tuner, simply use the generatered ./.pseudotv/xmltv.xml file for EPG data. PseudoTV will automatically refresh your Plex server's EPG data and channel mappings (if specified to do so in settings) when configuring channels via the Web UI. Ensure your FFMPEG path is set correctly via the Web UI, and enjoy!</Description>
dizqueTV will show up as a HDHomeRun device within Plex. When configuring your Plex Tuner, simply use the generatered ./.dizque/xmltv.xml file for EPG data. dizqueTV will automatically refresh your Plex server's EPG data and channel mappings (if specified to do so in settings) when configuring channels via the Web UI. Ensure your FFMPEG path is set correctly via the Web UI, and enjoy!</Description>
<Networking>
<Mode>host</Mode>
<Publish>
@ -37,8 +37,8 @@ PseudoTV will show up as a HDHomeRun device within Plex. When configuring your P
</Networking>
<Data>
<Volume>
<HostDir>/mnt/user/appdata/pseudotv/</HostDir>
<ContainerDir>/home/node/app/.pseudotv</ContainerDir>
<HostDir>/mnt/user/appdata/dizquetv/</HostDir>
<ContainerDir>/home/node/app/.dizquetv</ContainerDir>
<Mode>rw</Mode>
</Volume>
</Data>
@ -58,5 +58,5 @@ PseudoTV will show up as a HDHomeRun device within Plex. When configuring your P
<Config Name="Webui &amp;amp; HDHR" Target="8000" Default="" Mode="tcp" Description="Container Port: 8000" Type="Port" Display="always" Required="false" Mask="false">8000</Config>
<Config Name="NVIDIA_VISIBLE_DEVICES" Target="NVIDIA_VISIBLE_DEVICES" Default="" Mode="" Description="Container Variable: NVIDIA_VISIBLE_DEVICES" Type="Variable" Display="always" Required="false" Mask="false">all</Config>
<Config Name="NVIDIA_DRIVER_CAPABILITIES" Target="NVIDIA_DRIVER_CAPABILITIES" Default="" Mode="" Description="Container Variable: NVIDIA_DRIVER_CAPABILITIES" Type="Variable" Display="always" Required="false" Mask="false">all</Config>
<Config Name="Appdata" Target="/home/node/app/.pseudotv" Default="" Mode="rw" Description="Container Path: /home/node/app/.pseudotv" Type="Path" Display="always" Required="false" Mask="false">/mnt/user/appdata/pseudotv/</Config>
<Config Name="Appdata" Target="/home/node/app/.dizquetv" Default="" Mode="rw" Description="Container Path: /home/node/app/.dizquetv" Type="Path" Display="always" Required="false" Mask="false">/mnt/user/appdata/dizquetv/</Config>
</Container>

View File

@ -1,8 +1,8 @@
<?xml version="1.0"?>
<Container version="2">
<Name>pseudotv</Name>
<Repository>defendore/pseudotv:latest</Repository>
<Registry>https://hub.docker.com/r/defendore/pseudotv</Registry>
<Name>dizquetv</Name>
<Repository>vexorian/dizquetv:latest</Repository>
<Registry>https://hub.docker.com/r/vexorian/dizquetv</Registry>
<Network>host</Network>
<MyIP/>
<Shell>bash</Shell>
@ -13,7 +13,7 @@
<Category/>
<WebUI>http://[IP]:[PORT:8000]</WebUI>
<TemplateURL/>
<Icon>https://raw.githubusercontent.com/DEFENDORe/pseudotv/master/resources/pseudotv.png</Icon>
<Icon>https://raw.githubusercontent.com/vexorian/dizquetv/master/resources/dizquetv.png</Icon>
<ExtraParams></ExtraParams>
<PostArgs/>
<CPUset/>
@ -33,8 +33,8 @@
</Networking>
<Data>
<Volume>
<HostDir>/mnt/user/appdata/pseudotv/</HostDir>
<ContainerDir>/home/node/app/.pseudotv</ContainerDir>
<HostDir>/mnt/user/appdata/dizquetv/</HostDir>
<ContainerDir>/home/node/app/.dizquetv</ContainerDir>
<Mode>rw</Mode>
</Volume>
</Data>
@ -42,5 +42,5 @@
</Environment>
<Labels/>
<Config Name="Webui &amp;amp; HDHR" Target="8000" Default="" Mode="tcp" Description="Container Port: 8000" Type="Port" Display="always" Required="false" Mask="false">8000</Config>
<Config Name="Appdata" Target="/home/node/app/.pseudotv" Default="" Mode="rw" Description="Container Path: /home/node/app/.pseudotv" Type="Path" Display="always" Required="false" Mask="false">/mnt/user/appdata/pseudotv/</Config>
<Config Name="Appdata" Target="/home/node/app/.dizquetv" Default="" Mode="rw" Description="Container Path: /home/node/app/.dizquetv" Type="Path" Display="always" Required="false" Mask="false">/mnt/user/appdata/dizquetv/</Config>
</Container>

BIN
resources/dizquetv.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

View File

@ -10,7 +10,7 @@ function api(db, xmltvInterval) {
let router = express.Router()
router.get('/api/version', (req, res) => {
res.send( { "pseudotv" : constants.VERSION_NAME } )
res.send( { "dizquetv" : constants.VERSION_NAME } )
})
// Plex Servers
@ -179,11 +179,11 @@ function api(db, xmltvInterval) {
let channels = db['channels'].find()
var data = "#EXTM3U\n"
for (var i = 0; i < channels.length; i++) {
data += `#EXTINF:0 tvg-id="${channels[i].number}" tvg-name="${channels[i].name}" tvg-logo="${channels[i].icon}" group-title="PseudoTV",${channels[i].name}\n`
data += `#EXTINF:0 tvg-id="${channels[i].number}" tvg-name="${channels[i].name}" tvg-logo="${channels[i].icon}" group-title="dizqueTV",${channels[i].name}\n`
data += `${req.protocol}://${req.get('host')}/video?channel=${channels[i].number}\n`
}
if (channels.length === 0) {
data += `#EXTINF:0 tvg-id="1" tvg-name="PseudoTV" tvg-logo="https://raw.githubusercontent.com/DEFENDORe/pseudotv/master/resources/pseudotv.png" group-title="PseudoTV",PseudoTV\n`
data += `#EXTINF:0 tvg-id="1" tvg-name="dizqueTV" tvg-logo="https://raw.githubusercontent.com/vexorian/dizquetv/master/resources/dizquetv.png" group-title="dizqueTV",dizqueTV\n`
data += `${req.protocol}://${req.get('host')}/setup\n`
}
res.send(data)

View File

@ -17,15 +17,27 @@
* but with time it will be worth it, really.
*
***/
const TARGET_VERSION = 200;
const TARGET_VERSION = 300;
const STEPS = [
// [v, v2, x] : if the current version is v, call x(db), and version becomes v2
[ 0, 100, (db) => basicDB(db) ],
[ 100, 200, (db) => commercialsRemover(db) ],
[ 200, 300, (db) => appNameChange(db) ],
]
function appNameChange(db) {
let xmltv = db['xmltv-settings'].find()
if (xmltv.length > 0) {
xmltv = xmltv[0];
if (typeof(xmltv.file) !== 'undefined') {
xmltv.file = xmltv.file.replace(/\.pseudotv/, ".dizquetv");
db['xmltv-settings'].update( { "_id" : xmltv._id} , xmltv );
}
}
}
function basicDB(db) {
//this one should either try recovering the db from a very old version
//or buildl a completely empty db at version 0
@ -319,7 +331,7 @@ function initDB(db) {
console.log("Done migrating db to version : " + dbVersion.version);
} catch (e) {
console.log("Error during migration. Sorry, we can't continue. Wiping out your .pseudotv folder might be a workaround, but that means you lose all your settings.", e);
console.log("Error during migration. Sorry, we can't continue. Wiping out your .dizquetv folder might be a workaround, but that means you lose all your settings.", e);
throw Error("Migration error, step=" + dbVersion.version);
}
} else {

View File

@ -1,7 +1,7 @@
const spawn = require('child_process').spawn
const events = require('events')
//they can customize this by modifying the picture in .pseudotv folder
//they can customize this by modifying the picture in .dizquetv folder
const MAXIMUM_ERROR_DURATION_MS = 60000;
@ -319,7 +319,7 @@ class FFMPEG extends events.EventEmitter {
}
ffmpegArgs.push(`-metadata`,
`service_provider="PseudoTV"`,
`service_provider="dizqueTV"`,
`-metadata`,
`service_name="${this.channel.name}`,
`-f`, `mpegts`);

View File

@ -50,7 +50,7 @@ function hdhr(db) {
for (let i = 0, l = channels.length; i < l; i++)
lineup.push({ GuideNumber: channels[i].number.toString(), GuideName: channels[i].name, URL: `${req.protocol}://${req.get('host')}/video?channel=${channels[i].number}` })
if (lineup.length === 0)
lineup.push({ GuideNumber: '1', GuideName: 'PseudoTV', URL: `${req.protocol}://${req.get('host')}/setup` })
lineup.push({ GuideNumber: '1', GuideName: 'dizqueTV', URL: `${req.protocol}://${req.get('host')}/setup` })
res.send(JSON.stringify(lineup))
})
@ -60,14 +60,14 @@ function hdhr(db) {
function getDevice(db, host) {
let hdhrSettings = db['hdhr-settings'].find()[0]
var device = {
FriendlyName: "PseudoTV",
Manufacturer: "PseudoTV - Silicondust",
ManufacturerURL: "https://gitlab.org/DEFENDORe/pseudotv-plex",
FriendlyName: "dizqueTV",
Manufacturer: "dizqueTV - Silicondust",
ManufacturerURL: "https://github.com/vexorian/dizquetv",
ModelNumber: "HDTC-2US",
FirmwareName: "hdhomeruntc_atsc",
TunerCount: hdhrSettings.tunerCount,
FirmwareVersion: "20170930",
DeviceID: 'PseudoTV',
DeviceID: 'dizqueTV',
DeviceAuth: "",
BaseURL: `${host}`,
LineupURL: `${host}/lineup.json`
@ -82,7 +82,7 @@ function getDevice(db, host) {
</specVersion>
<device>
<deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType>
<friendlyName>PseudoTV</friendlyName>
<friendlyName>dizqueTV</friendlyName>
<manufacturer>Silicondust</manufacturer>
<modelName>HDTC-2US</modelName>
<modelNumber>HDTC-2US</modelNumber>

View File

@ -10,9 +10,9 @@ class Plex {
}
this._headers = {
'Accept': 'application/json',
'X-Plex-Device': 'PseudoTV',
'X-Plex-Device-Name': 'PseudoTV',
'X-Plex-Product': 'PseudoTV',
'X-Plex-Device': 'dizqueTV',
'X-Plex-Device-Name': 'dizqueTV',
'X-Plex-Product': 'dizqueTV',
'X-Plex-Version': '0.1',
'X-Plex-Client-Identifier': 'rg14zekk3pa5zp4safjwaa8z',
'X-Plex-Platform': 'Chrome',

View File

@ -7,8 +7,8 @@ class PlexTranscoder {
this.device = "channel-" + channel.number;
this.deviceName = this.device;
this.clientIdentifier = this.session.replace(/-/g,"").slice(0,16) + "-org-pseudotv-" + process.platform;
this.product = "PseudoTV";
this.clientIdentifier = this.session.replace(/-/g,"").slice(0,16) + "-org-dizquetv-" + process.platform;
this.product = "dizqueTV";
this.settings = settings

133
src/svg/dizquetv.svg Normal file
View File

@ -0,0 +1,133 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="200"
height="200"
viewBox="0 0 52.9168 52.916668"
version="1.1"
id="svg8"
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"
sodipodi:docname="dizquetv.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.5768297"
inkscape:cx="-44.708187"
inkscape:cy="32.377644"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="1920"
inkscape:window-height="1056"
inkscape:window-x="0"
inkscape:window-y="24"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Capa 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-244.08278)">
<rect
style="opacity:1;fill:#e6e6e6;fill-opacity:1;stroke:none;stroke-width:1.46499991;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4712"
width="51.087337"
height="48.801544"
x="3.6474326"
y="245.61409"
transform="rotate(0.63279348)" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:7.93639898px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:0.81132076;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="16.805246"
y="257.03284"
id="text4692"
transform="rotate(0.63279348)"><tspan
sodipodi:role="line"
id="tspan4690"
x="16.805246"
y="257.03284"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29093266px;font-family:'Liberation Serif';-inkscape-font-specification:'Liberation Serif';fill:#000000;fill-opacity:0.81132076;stroke-width:0.26458332px">dizqueT</tspan></text>
<g
id="g4581"
style="fill:#1f1f1f;fill-opacity:1;stroke-width:0.85952657"
transform="matrix(1.1634312,0,0,1.1634312,-78.839147,-18.96312)">
<rect
transform="rotate(-0.94645665)"
y="239.28041"
x="65.156158"
height="27.75024"
width="41.471352"
id="rect4524"
style="opacity:1;fill:#1f1f1f;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
</g>
<rect
style="opacity:1;fill:#9cbc28;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4518"
width="9.3074493"
height="20.941759"
x="7.8814092"
y="263.77377"
transform="rotate(0.52601423)" />
<rect
y="263.45377"
x="21.544861"
height="20.941759"
width="9.3074484"
id="rect4520"
style="opacity:1;fill:#289bbc;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
transform="rotate(1.4727576)" />
<rect
style="opacity:1;fill:#bc289b;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4522"
width="9.3074474"
height="20.941761"
x="7.5537415"
y="265.03363"
transform="rotate(-3.2986122)" />
<circle
style="opacity:1;fill:#6a6a6a;fill-opacity:0.86792453;stroke:none;stroke-width:1.46499991;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path4568"
cx="41.451012"
cy="266.38718"
r="2.9085779" />
<circle
r="2.9085779"
cy="277.88605"
cx="42.825504"
id="circle4570"
style="opacity:1;fill:#6a6a6a;fill-opacity:0.86792453;stroke:none;stroke-width:1.46499991;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#1f1f1f;stroke-width:0.86500216;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 28.843321,248.69894 5.63385,9.46891 1.848035,-5.42662"
id="path5993"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@ -7,7 +7,6 @@
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="1920"
@ -15,7 +14,7 @@
viewBox="0 0 507.99999 285.75001"
version="1.1"
id="svg8"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"
sodipodi:docname="generic-error-screen.svg"
inkscape:export-filename="/home/vx/dev/pseudotv/resources/generic-error-screen.png"
inkscape:export-xdpi="96"
@ -30,8 +29,8 @@
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.43386034"
inkscape:cx="1327.3621"
inkscape:cy="796.69727"
inkscape:cx="1055.6646"
inkscape:cy="492.08878"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
@ -49,7 +48,7 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
@ -65,646 +64,6 @@
height="285.75"
x="0"
y="11.24997" />
<image
y="81.529213"
x="214.88797"
id="image1079"
xlink:href="data:image/png;base64,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
d038AAAAAElFTkSuQmCC
"
preserveAspectRatio="none"
height="86.535645"
width="86.851501"
style="stroke-width:1.52597129" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:11.11943626px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458335px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
@ -727,5 +86,33 @@ d038AAAAAElFTkSuQmCC
y="230.46899"
style="font-size:20.75628471px;fill:#ffffff;stroke-width:0.26458335px"
id="tspan1107">Try again later.</tspan></text>
<g
id="g6050"
transform="translate(0,-9.6343724)">
<rect
transform="rotate(0.52601418)"
y="85.000603"
x="214.56714"
height="73.832573"
width="32.814484"
id="rect4518"
style="opacity:1;fill:#9cbc28;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<rect
transform="rotate(1.4727575)"
style="opacity:1;fill:#289bbc;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4520"
width="32.81448"
height="73.832573"
x="248.74632"
y="80.901688" />
<rect
transform="rotate(-3.2986121)"
y="103.78287"
x="269.35843"
height="73.832588"
width="32.814476"
id="rect4522"
style="opacity:1;fill:#bc289b;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -21,9 +21,9 @@ function video(db) {
return
}
console.log(`\r\nStream starting. Channel: 1 (PseudoTV)`)
console.log(`\r\nStream starting. Channel: 1 (dizqueTV)`)
let ffmpeg = new FFMPEG_TEXT(ffmpegSettings, 'PseudoTV (No Channels Configured)', 'Configure your channels using the PseudoTV Web UI')
let ffmpeg = new FFMPEG_TEXT(ffmpegSettings, 'dizqueTV (No Channels Configured)', 'Configure your channels using the dizqueTV Web UI')
ffmpeg.on('data', (data) => { res.write(data) })
@ -38,7 +38,7 @@ function video(db) {
res.on('close', () => { // on HTTP close, kill ffmpeg
ffmpeg.kill()
console.log(`\r\nStream ended. Channel: 1 (PseudoTV)`)
console.log(`\r\nStream ended. Channel: 1 (dizqueTV)`)
})
})
// Continuously stream video to client. Leverage ffmpeg concat for piecing together videos

View File

@ -14,13 +14,13 @@ function WriteXMLTV(channels, xmlSettings) {
ws.on('error', (err) => { reject(err) })
_writeDocStart(xw)
async function middle() {
if (channels.length === 0) { // Write Dummy PseudoTV Channel if no channel exists
_writeChannels(xw, [{ number: 1, name: "PseudoTV", icon: "https://raw.githubusercontent.com/DEFENDORe/pseudotv/master/resources/pseudotv.png" }])
if (channels.length === 0) { // Write Dummy dizqueTV Channel if no channel exists
_writeChannels(xw, [{ number: 1, name: "dizqueTV", icon: "https://raw.githubusercontent.com/vexorain/dizquetv/master/resources/dizquetv.png" }])
let program = {
program: {
type: 'movie',
title: 'No Channels Configured',
summary: 'Configure your channels using the PseudoTV Web UI.'
summary: 'Configure your channels using the dizqueTV Web UI.'
},
channel: '1',
start: date,

View File

@ -7,7 +7,7 @@ require('./ext/angularjs-scroll-glue')
var app = angular.module('myApp', ['ngRoute', 'angularLazyImg', 'dndLists', 'luegg.directives'])
app.service('plex', require('./services/plex'))
app.service('pseudotv', require('./services/pseudotv'))
app.service('dizquetv', require('./services/dizquetv'))
app.directive('plexSettings', require('./directives/plex-settings'))
app.directive('ffmpegSettings', require('./directives/ffmpeg-settings'))

View File

@ -1,15 +1,15 @@
module.exports = function ($scope, pseudotv) {
module.exports = function ($scope, dizquetv) {
$scope.channels = []
$scope.showChannelConfig = false
$scope.selectedChannel = null
$scope.selectedChannelIndex = -1
pseudotv.getChannels().then((channels) => {
dizquetv.getChannels().then((channels) => {
$scope.channels = channels
})
$scope.removeChannel = (channel) => {
if (confirm("Are you sure to delete channel: " + channel.name + "?")) {
pseudotv.removeChannel(channel).then((channels) => {
dizquetv.removeChannel(channel).then((channels) => {
$scope.channels = channels
})
}
@ -17,11 +17,11 @@ module.exports = function ($scope, pseudotv) {
$scope.onChannelConfigDone = (channel) => {
if (typeof channel !== 'undefined') {
if ($scope.selectedChannelIndex == -1) { // add new channel
pseudotv.addChannel(channel).then((channels) => {
dizquetv.addChannel(channel).then((channels) => {
$scope.channels = channels
})
} else { // update existing channel
pseudotv.updateChannel(channel).then((channels) => {
dizquetv.updateChannel(channel).then((channels) => {
$scope.channels = channels
})
}

View File

@ -1,7 +1,7 @@
module.exports = function ($scope, pseudotv) {
$scope.version = "Getting PseudoTV version..."
pseudotv.getVersion().then((version) => {
$scope.version = version.pseudotv
module.exports = function ($scope, dizquetv) {
$scope.version = "Getting dizqueTV version..."
dizquetv.getVersion().then((version) => {
$scope.version = version.dizquetv
})

View File

@ -22,7 +22,7 @@ module.exports = function ($timeout, $location) {
scope.channel.fillerRepeatCooldown = 30 * 60 * 1000;
scope.channel.fallback = [];
scope.isNewChannel = true
scope.channel.icon = `${$location.protocol()}://${location.host}/images/pseudotv.png`
scope.channel.icon = `${$location.protocol()}://${location.host}/images/dizquetv.png`
scope.channel.disableFillerOverlay = true;
scope.channel.iconWidth = 120
scope.channel.iconDuration = 60

View File

@ -1,4 +1,4 @@
module.exports = function (pseudotv) {
module.exports = function (dizquetv) {
return {
restrict: 'E',
templateUrl: 'templates/ffmpeg-settings.html',
@ -6,16 +6,16 @@
scope: {
},
link: function (scope, element, attrs) {
pseudotv.getFfmpegSettings().then((settings) => {
dizquetv.getFfmpegSettings().then((settings) => {
scope.settings = settings
})
scope.updateSettings = (settings) => {
pseudotv.updateFfmpegSettings(settings).then((_settings) => {
dizquetv.updateFfmpegSettings(settings).then((_settings) => {
scope.settings = _settings
})
}
scope.resetSettings = (settings) => {
pseudotv.resetFfmpegSettings(settings).then((_settings) => {
dizquetv.resetFfmpegSettings(settings).then((_settings) => {
scope.settings = _settings
})
}

View File

@ -1,4 +1,4 @@
module.exports = function (pseudotv, $timeout) {
module.exports = function (dizquetv, $timeout) {
return {
restrict: 'E',
templateUrl: 'templates/hdhr-settings.html',
@ -6,7 +6,7 @@ module.exports = function (pseudotv, $timeout) {
scope: {
},
link: function (scope, element, attrs) {
pseudotv.getHdhrSettings().then((settings) => {
dizquetv.getHdhrSettings().then((settings) => {
scope.settings = settings
})
scope.updateSettings = (settings) => {
@ -19,12 +19,12 @@ module.exports = function (pseudotv, $timeout) {
$timeout(() => {
scope.error = null
}, 3500)
pseudotv.updateHdhrSettings(settings).then((_settings) => {
dizquetv.updateHdhrSettings(settings).then((_settings) => {
scope.settings = _settings
})
}
scope.resetSettings = (settings) => {
pseudotv.resetHdhrSettings(settings).then((_settings) => {
dizquetv.resetHdhrSettings(settings).then((_settings) => {
scope.settings = _settings
})
}

View File

@ -1,4 +1,4 @@
module.exports = function (plex, pseudotv, $timeout) {
module.exports = function (plex, dizquetv, $timeout) {
return {
restrict: 'E',
templateUrl: 'templates/plex-library.html',
@ -41,7 +41,7 @@ module.exports = function (plex, pseudotv, $timeout) {
}, 0)
})
}
pseudotv.getPlexServers().then((servers) => {
dizquetv.getPlexServers().then((servers) => {
if (servers.length === 0) {
scope.noServers = true
return

View File

@ -1,11 +1,11 @@
module.exports = function (plex, pseudotv, $timeout) {
module.exports = function (plex, dizquetv, $timeout) {
return {
restrict: 'E',
templateUrl: 'templates/plex-settings.html',
replace: true,
scope: {},
link: function (scope, element, attrs) {
pseudotv.getPlexServers().then((servers) => {
dizquetv.getPlexServers().then((servers) => {
scope.servers = servers
})
scope.addPlexServer = function () {
@ -15,10 +15,10 @@ module.exports = function (plex, pseudotv, $timeout) {
result.servers.forEach((server) => {
// add in additional settings
server.arGuide = true
server.arChannels = false // should not be enabled unless PseudoTV tuner already added to plex
pseudotv.addPlexServer(server)
server.arChannels = false // should not be enabled unless dizqueTV tuner already added to plex
dizquetv.addPlexServer(server)
});
return pseudotv.getPlexServers()
return dizquetv.getPlexServers()
}).then((servers) => {
scope.$apply(() => {
scope.servers = servers
@ -35,21 +35,21 @@ module.exports = function (plex, pseudotv, $timeout) {
})
}
scope.deletePlexServer = (x) => {
pseudotv.removePlexServer(x)
dizquetv.removePlexServer(x)
.then((servers) => {
scope.servers = servers
})
}
pseudotv.getPlexSettings().then((settings) => {
dizquetv.getPlexSettings().then((settings) => {
scope.settings = settings
})
scope.updateSettings = (settings) => {
pseudotv.updatePlexSettings(settings).then((_settings) => {
dizquetv.updatePlexSettings(settings).then((_settings) => {
scope.settings = _settings
})
}
scope.resetSettings = (settings) => {
pseudotv.resetPlexSettings(settings).then((_settings) => {
dizquetv.resetPlexSettings(settings).then((_settings) => {
scope.settings = _settings
})
}

View File

@ -1,4 +1,4 @@
module.exports = function (pseudotv) {
module.exports = function (dizquetv) {
return {
restrict: 'E',
templateUrl: 'templates/xmltv-settings.html',
@ -6,16 +6,16 @@ module.exports = function (pseudotv) {
scope: {
},
link: function (scope, element, attrs) {
pseudotv.getXmltvSettings().then((settings) => {
dizquetv.getXmltvSettings().then((settings) => {
scope.settings = settings
})
scope.updateSettings = (settings) => {
pseudotv.updateXmltvSettings(settings).then((_settings) => {
dizquetv.updateXmltvSettings(settings).then((_settings) => {
scope.settings = _settings
})
}
scope.resetSettings = (settings) => {
pseudotv.resetXmltvSettings(settings).then((_settings) => {
dizquetv.resetXmltvSettings(settings).then((_settings) => {
scope.settings = _settings
})
}

View File

@ -1,7 +1,7 @@
<html>
<head>
<title>PseudoTV</title>
<title>dizqueTV</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">
<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
@ -11,9 +11,9 @@
<body ng-app="myApp" style="min-width: 340px;">
<div class="container">
<h1>PseudoTV
<h1>dizqueTV
<small class="pull-right" style="padding: 5px;">
<a href="https://github.com/DEFENDORe/pseudotv">
<a href="https://github.com/vexorian/dizquetv">
<span class="fa fa-github text-sm"></span>
</a>
</small>

View File

@ -121,7 +121,7 @@
ng-options="o.value as o.description for o in errorAudios" />
</div>
<small id="errorHelp" class="form-text text-muted">If there are issues playing a video, pseudoTV will try to use an error screen as a placeholder while retrying loading the video every 60 seconds.</small>
<small id="errorHelp" class="form-text text-muted">If there are issues playing a video, dizqueTV will try to use an error screen as a placeholder while retrying loading the video every 60 seconds.</small>
</div>
@ -130,7 +130,7 @@
<div class="form-group">
<input id="enableNormalizeResolution" type="checkbox" ng-model="settings.normalizeResolution" ng-disabled="isTranscodingNotNeeded()" />
<label for="enableNormalizeResolution">Normalize Resolution</label>
<small class="form-text text-muted">Some clients experience issues when the video stream changes resolution. This option will make pseudoTV convert all videos to the preferred resolution selected above.
<small class="form-text text-muted">Some clients experience issues when the video stream changes resolution. This option will make dizqueTV convert all videos to the preferred resolution selected above.
</small>
</div>
</div>

View File

@ -9,7 +9,7 @@
<th width="120">Version</th>
</tr>
<tr>
<td>PseudoTV</td>
<td>dizqueTV</td>
<td>{{version}}</td>
</tr>
<!-- coming soon, ffmpeg version, nodejs version, plex version, whatever can be used to help debug things-->

View File

@ -5,7 +5,7 @@ module.exports = function ($http, $window, $interval) {
login: async () => {
const headers = {
'Accept': 'application/json',
'X-Plex-Product': 'PseudoTV',
'X-Plex-Product': 'dizqueTV',
'X-Plex-Version': 'Plex OAuth',
'X-Plex-Client-Identifier': 'rg14zekk3pa5zp4safjwaa8z',
'X-Plex-Model': 'Plex OAuth'