Compare commits
25 Commits
2023-11-28
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
826f21763d | ||
|
|
fd998e4834 | ||
|
|
564dceb303 | ||
|
|
30cc9f539f | ||
|
|
3f7dcf6e77 | ||
|
|
8be607a81b | ||
|
|
1ed31d22e9 | ||
|
|
1abdaa68f1 | ||
|
|
0f4dd1c464 | ||
|
|
196033844a | ||
|
|
7e9db8c1f6 | ||
|
|
f59cca4ea4 | ||
|
|
0ea92271cb | ||
|
|
08c81f97a2 | ||
|
|
869987bdb1 | ||
|
|
e0fda77c56 | ||
|
|
cdc13a0267 | ||
|
|
56a4f3fdd8 | ||
|
|
bc7b70ef12 | ||
|
|
323caa2739 | ||
|
|
c360ddae05 | ||
|
|
32ddac1df0 | ||
|
|
88f03638fb | ||
|
|
6898b9f31d | ||
|
|
7665dcf6e8 |
13
.github/workflows/README.md
vendored
Normal file
13
.github/workflows/README.md
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
# The workflow
|
||||
|
||||
edge main
|
||||
^ \ ^
|
||||
| \------2--\ |
|
||||
1 \ 3
|
||||
| \ |
|
||||
development <-4-- patch
|
||||
|
||||
1. Releasing a new 'edge' version.
|
||||
2. Moving an 'edge' version to stable.
|
||||
3. Releasing a stable version
|
||||
4. Aligning bug fixes with the new features.
|
||||
2
.github/workflows/development-binaries.yaml
vendored
2
.github/workflows/development-binaries.yaml
vendored
@ -3,7 +3,7 @@ name: Development Binaries
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev/1.5.x
|
||||
- development
|
||||
|
||||
jobs:
|
||||
binaries:
|
||||
|
||||
2
.github/workflows/development-tag.yaml
vendored
2
.github/workflows/development-tag.yaml
vendored
@ -3,7 +3,7 @@ name: Development Tag
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev/1.5.x
|
||||
- development
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
|
||||
@ -2,73 +2,64 @@
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
We pledge to make our community welcoming, safe, and equitable for all.
|
||||
|
||||
## Our Standards
|
||||
We are committed to fostering an environment that respects and promotes the dignity, rights, and contributions of all individuals, regardless of characteristics including race, ethnicity, caste, color, age, physical characteristics, neurodiversity, disability, sex or gender, gender identity or expression, sexual orientation, language, philosophy or religion, national or social origin, socio-economic position, level of education, or other status. The same privileges of participation are extended to everyone who participates in good faith and in accordance with this Covenant.
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
## Encouraged Behaviors
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
While acknowledging differences in social norms, we all strive to meet our community's expectations for positive behavior. We also understand that our words and actions may be interpreted differently than we intend based on culture, background, or native language.
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
With these considerations in mind, we agree to behave mindfully toward each other and act in ways that center our shared values, including:
|
||||
|
||||
## Our Responsibilities
|
||||
1. Respecting the **purpose of our community**, our activities, and our ways of gathering.
|
||||
2. Engaging **kindly and honestly** with others.
|
||||
3. Respecting **different viewpoints** and experiences.
|
||||
4. **Taking responsibility** for our actions and contributions.
|
||||
5. Gracefully giving and accepting **constructive feedback**.
|
||||
6. Committing to **repairing harm** when it occurs.
|
||||
7. Behaving in other ways that promote and sustain the **well-being of our community**.
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
## Restricted Behaviors
|
||||
|
||||
## Scope
|
||||
We agree to restrict the following behaviors in our community. Instances, threats, and promotion of these behaviors are violations of this Code of Conduct.
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
1. **Harassment.** Violating explicitly expressed boundaries or engaging in unnecessary personal attention after any clear request to stop.
|
||||
2. **Character attacks.** Making insulting, demeaning, or pejorative comments directed at a community member or group of people.
|
||||
3. **Stereotyping or discrimination.** Characterizing anyone’s personality or behavior on the basis of immutable identities or traits.
|
||||
4. **Sexualization.** Behaving in a way that would generally be considered inappropriately intimate in the context or purpose of the community.
|
||||
5. **Violating confidentiality**. Sharing or acting on someone's personal or private information without their permission.
|
||||
6. **Endangerment.** Causing, encouraging, or threatening violence or other harm toward any person or group.
|
||||
7. Behaving in other ways that **threaten the well-being** of our community.
|
||||
|
||||
## Enforcement
|
||||
### Other Restrictions
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at vexorian@gmail.com. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
1. **Misleading identity.** Impersonating someone else for any reason, or pretending to be someone else to evade enforcement actions.
|
||||
2. **Failing to credit sources.** Not properly crediting the sources of content you contribute.
|
||||
3. **Promotional materials**. Sharing marketing or other commercial content in a way that is outside the norms of the community.
|
||||
4. **Irresponsible communication.** Failing to responsibly present content which includes, links or describes any other restricted behaviors.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
## 'AI' policy
|
||||
|
||||
There are ways in which a LLM-based tool can be helpful such as when searching the web or for learning. More so, tools like github and google themselves are being modified to railroad users into using LLM-based tools. It'd be really impractical and neigh-impossible to ban 'AI' altogether from being used during the development of a contribution and that's not really the purpose of this policy.
|
||||
|
||||
HOWEVER, from a legal standpoint, it is too difficult to know the origin of LLM-generated code so there are risks of it infringing on copyright. And from a pragmatic stand point, we want to be able to trust the quality of the code. That is to say, we do not want contributions where the bulk of the code was AI-generated or or where the contributors themselves do not understand the code being pushed. This is also in relation to items 4 and 5 of the encouraged behaviors. We want contributors that can vouch for the code they contribute and can receive and act on constructive feedback to it.
|
||||
|
||||
Note that this is only a policy affecting Pull Requests to this project. This project is released under a permissive FOSS licence, so there's nothing really that can stop forks from having a different policy on this and any individual is allowed to create such a fork should they want to.
|
||||
|
||||
## Reporting an Issue
|
||||
|
||||
Tensions can occur between community members even when they are trying their best to collaborate. Not every conflict represents a code of conduct violation, and this Code of Conduct reinforces encouraged behaviors and norms that can help avoid conflicts and minimize harm.
|
||||
|
||||
When an incident does occur, it is important to report it promptly. To report a possible violation, may be
|
||||
reported by contacting the project team at vexorian@gmail.com
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 3.0,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html . The AI policy is a modification.
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
|
||||
15
README.md
15
README.md
@ -1,4 +1,4 @@
|
||||
# dizqueTV 1.5.3-development
|
||||
# dizqueTV 1.5.5
|
||||
  
|
||||
|
||||
Create live TV channel streams from media on your Plex servers.
|
||||
@ -25,6 +25,7 @@ EPG (Guide Information) data is stored to `.dizquetv/xmltv.xml`
|
||||
- Subtitle support.
|
||||
- Auto deinterlace any Plex media not marked `"scanType": "progressive"`
|
||||
- Can be configured to completely force Direct play, if you are ready for the caveats.
|
||||
- It's up to you if the channels have a life of their own and act as if they continued playing when you weren't watching them or if you want "on-demand" channels that stop their schedules while not being watched.
|
||||
|
||||
## Limitations
|
||||
|
||||
@ -32,6 +33,7 @@ EPG (Guide Information) data is stored to `.dizquetv/xmltv.xml`
|
||||
- dizqueTV does not currently watch your Plex server for media updates/changes. You must manually remove and re-add your programs for any changes to take effect. Same goes for Plex server changes (changing IP, port, etc).. You'll have to update the server settings manually in that case.
|
||||
- Most players (including Plex) will break after switching episodes if video / audio format is too different. dizqueTV can be configured to use ffmpeg transcoding to prevent this, but that costs resources.
|
||||
- If you configure Plex DVR, it will always be recording and transcoding the channel's contents.
|
||||
- In its current state, dizquetv is intended for private use only and you should be discouraged from running dizqueTV in any capacity where other users can have access to dizqueTV's ports. You can use Plex's iptv player feature to share dizqueTV streams or you'll have to come up with some work arounds to make sure that streams can be played without ever actually exposing the dizquetv port to the outside world. Please use it with care, consider exposing dizqueTV's ports as something only advanced users who know what they are doing should try.
|
||||
|
||||
## Releases
|
||||
|
||||
@ -75,4 +77,13 @@ npm run dev-server
|
||||
* Original pseudotv-Plex code was released under [MIT license (c) 2020 Dan Ferguson](https://github.com/DEFENDORe/pseudotv/blob/665e71e24ee5e93d9c9c90545addb53fdc235ff6/LICENSE)
|
||||
* dizqueTV's improvements are released under zlib license (c) 2020 Victor Hugo Soliz Kuncar
|
||||
* FontAwesome: [https://fontawesome.com/license/free](https://archive.fo/PRqis)
|
||||
* Bootstrap: https://github.com/twbs/bootstrap/blob/v4.4.1/LICENSE
|
||||
* Bootstrap: https://github.com/twbs/bootstrap/blob/v4.4.1/LICENSE
|
||||
|
||||
## Thanks
|
||||
|
||||
* DEFENDORe , George and everyone that worked on PseudoTV
|
||||
* Ahmed Said Al-Busaidi , for reporting exploits in advance before publishing in exploit-db.
|
||||
* Nathan for working on automation addons.
|
||||
* Timebomb, Rafael for the contributions during dizqueTV's active days.
|
||||
|
||||
|
||||
10
index.js
10
index.js
@ -31,6 +31,7 @@ const OnDemandService = require("./src/services/on-demand-service");
|
||||
const ProgrammingService = require("./src/services/programming-service");
|
||||
const ActiveChannelService = require('./src/services/active-channel-service')
|
||||
const ProgramPlayTimeDB = require('./src/dao/program-play-time-db')
|
||||
const FfmpegSettingsService = require('./src/services/ffmpeg-settings-service')
|
||||
|
||||
const onShutdown = require("node-graceful-shutdown").onShutdown;
|
||||
|
||||
@ -50,12 +51,16 @@ if (NODE < 12) {
|
||||
console.error(`WARNING: Your nodejs version ${process.version} is lower than supported. dizqueTV has been tested best on nodejs 12.16.`);
|
||||
}
|
||||
|
||||
|
||||
unlockPath = false;
|
||||
for (let i = 0, l = process.argv.length; i < l; i++) {
|
||||
if ((process.argv[i] === "-p" || process.argv[i] === "--port") && i + 1 !== l)
|
||||
process.env.PORT = process.argv[i + 1]
|
||||
if ((process.argv[i] === "-d" || process.argv[i] === "--database") && i + 1 !== l)
|
||||
process.env.DATABASE = process.argv[i + 1]
|
||||
|
||||
if (process.argv[i] === "--unlock") {
|
||||
unlockPath = true;
|
||||
}
|
||||
}
|
||||
|
||||
process.env.DATABASE = process.env.DATABASE || path.join(".", ".dizquetv")
|
||||
@ -101,6 +106,7 @@ channelService = new ChannelService(channelDB);
|
||||
fillerDB = new FillerDB( path.join(process.env.DATABASE, 'filler') , channelService );
|
||||
customShowDB = new CustomShowDB( path.join(process.env.DATABASE, 'custom-shows') );
|
||||
let programPlayTimeDB = new ProgramPlayTimeDB( path.join(process.env.DATABASE, 'play-cache') );
|
||||
let ffmpegSettingsService = new FfmpegSettingsService(db, unlockPath);
|
||||
|
||||
async function initializeProgramPlayTimeDB() {
|
||||
try {
|
||||
@ -284,7 +290,7 @@ app.use('/favicon.svg', express.static(
|
||||
app.use('/custom.css', express.static(path.join(process.env.DATABASE, 'custom.css')))
|
||||
|
||||
// API Routers
|
||||
app.use(api.router(db, channelService, fillerDB, customShowDB, xmltvInterval, guideService, m3uService, eventService ))
|
||||
app.use(api.router(db, channelService, fillerDB, customShowDB, xmltvInterval, guideService, m3uService, eventService, ffmpegSettingsService))
|
||||
app.use('/api/cache/images', cacheImageService.apiRouters())
|
||||
app.use('/' + fontAwesome, express.static(path.join(process.env.DATABASE, fontAwesome)))
|
||||
app.use('/' + bootstrap, express.static(path.join(process.env.DATABASE, bootstrap)))
|
||||
|
||||
@ -7,8 +7,8 @@
|
||||
* [ ] I have read the code of conduct.
|
||||
* [ ] I am submitting to the correct base branch
|
||||
<!--
|
||||
* Bug fixes must go to `dev/1.4.x`.
|
||||
* New features must go to `dev/1.5.x`.
|
||||
* Bug fixes for 'stable' versions must go to `patch`.
|
||||
* New features and fixes for 'edge' version must go to `development`.
|
||||
-->
|
||||
### Changes that modify the db structure
|
||||
|
||||
@ -19,3 +19,7 @@
|
||||
|
||||
* [ ] I understand that the feature may not be accepted if it doesn't fit the upstream app's planned design direction. But that in this case I am encouraged to share this as an available modification other users can use if they want.
|
||||
|
||||
### Code Standards
|
||||
|
||||
* [ ] I understand the code being contributed and it's purpose. <!-- Please read CODE_OF_CONDUCT for more info. -->
|
||||
|
||||
|
||||
27
src/api.js
27
src/api.js
@ -2,7 +2,6 @@
|
||||
const express = require('express')
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
const databaseMigration = require('./database-migration');
|
||||
const constants = require('./constants');
|
||||
const JSONStream = require('JSONStream');
|
||||
const FFMPEGInfo = require('./ffmpeg-info');
|
||||
@ -25,7 +24,7 @@ function safeString(object) {
|
||||
}
|
||||
|
||||
module.exports = { router: api }
|
||||
function api(db, channelService, fillerDB, customShowDB, xmltvInterval, guideService, _m3uService, eventService ) {
|
||||
function api(db, channelService, fillerDB, customShowDB, xmltvInterval, guideService, _m3uService, eventService, ffmpegSettingsService ) {
|
||||
let m3uService = _m3uService;
|
||||
const router = express.Router()
|
||||
const plexServerDB = new PlexServerDB(channelService, fillerDB, customShowDB, db);
|
||||
@ -529,7 +528,7 @@ function api(db, channelService, fillerDB, customShowDB, xmltvInterval, guideSe
|
||||
// FFMPEG SETTINGS
|
||||
router.get('/api/ffmpeg-settings', (req, res) => {
|
||||
try {
|
||||
let ffmpeg = db['ffmpeg-settings'].find()[0]
|
||||
let ffmpeg = ffmpegSettingsService.get();
|
||||
res.send(ffmpeg)
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
@ -538,9 +537,9 @@ function api(db, channelService, fillerDB, customShowDB, xmltvInterval, guideSe
|
||||
})
|
||||
router.put('/api/ffmpeg-settings', (req, res) => {
|
||||
try {
|
||||
db['ffmpeg-settings'].update({ _id: req.body._id }, req.body)
|
||||
let ffmpeg = db['ffmpeg-settings'].find()[0]
|
||||
let err = fixupFFMPEGSettings(ffmpeg);
|
||||
let result = ffmpegSettingsService.update(req.body);
|
||||
let err = result.error
|
||||
|
||||
if (typeof(err) !== 'undefined') {
|
||||
return res.status(400).send(err);
|
||||
}
|
||||
@ -555,7 +554,7 @@ function api(db, channelService, fillerDB, customShowDB, xmltvInterval, guideSe
|
||||
"level" : "info"
|
||||
}
|
||||
);
|
||||
res.send(ffmpeg)
|
||||
res.send(result.ffmpeg)
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
res.status(500).send("error");
|
||||
@ -576,10 +575,8 @@ function api(db, channelService, fillerDB, customShowDB, xmltvInterval, guideSe
|
||||
})
|
||||
router.post('/api/ffmpeg-settings', (req, res) => { // RESET
|
||||
try {
|
||||
let ffmpeg = databaseMigration.defaultFFMPEG() ;
|
||||
ffmpeg.ffmpegPath = req.body.ffmpegPath;
|
||||
db['ffmpeg-settings'].update({ _id: req.body._id }, ffmpeg)
|
||||
ffmpeg = db['ffmpeg-settings'].find()[0]
|
||||
let ffmpeg = ffmpegSettingsService.reset();
|
||||
|
||||
eventService.push(
|
||||
"settings-update",
|
||||
{
|
||||
@ -612,14 +609,6 @@ function api(db, channelService, fillerDB, customShowDB, xmltvInterval, guideSe
|
||||
|
||||
})
|
||||
|
||||
function fixupFFMPEGSettings(ffmpeg) {
|
||||
if (typeof(ffmpeg.maxFPS) === 'undefined') {
|
||||
ffmpeg.maxFPS = 60;
|
||||
} else if ( isNaN(ffmpeg.maxFPS) ) {
|
||||
return "maxFPS should be a number";
|
||||
}
|
||||
}
|
||||
|
||||
// PLEX SETTINGS
|
||||
router.get('/api/plex-settings', (req, res) => {
|
||||
try {
|
||||
|
||||
@ -35,5 +35,5 @@ module.exports = {
|
||||
// staying active, it checks every 5 seconds
|
||||
PLAYED_MONITOR_CHECK_FREQUENCY: 5*1000,
|
||||
|
||||
VERSION_NAME: "1.5.3-development"
|
||||
VERSION_NAME: "1.5.5"
|
||||
}
|
||||
|
||||
@ -20,7 +20,8 @@
|
||||
const path = require('path');
|
||||
var fs = require('fs');
|
||||
|
||||
const TARGET_VERSION = 803;
|
||||
const TARGET_VERSION = 805;
|
||||
const DAY_MS = 1000 * 60 * 60 * 24;
|
||||
|
||||
const STEPS = [
|
||||
// [v, v2, x] : if the current version is v, call x(db), and version becomes v2
|
||||
@ -43,6 +44,8 @@ const STEPS = [
|
||||
[ 800, 801, (db) => addImageCache(db) ],
|
||||
[ 801, 802, () => addGroupTitle() ],
|
||||
[ 802, 803, () => fixNonIntegerDurations() ],
|
||||
[ 803, 805, (db) => addFFMpegLock(db) ],
|
||||
[ 804, 805, (db) => addFFMpegLock(db) ],
|
||||
]
|
||||
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
@ -384,6 +387,7 @@ function ffmpeg() {
|
||||
//How default ffmpeg settings should look
|
||||
configVersion: 5,
|
||||
ffmpegPath: "/usr/bin/ffmpeg",
|
||||
ffmpegPathLockDate: new Date().getTime() + DAY_MS,
|
||||
threads: 4,
|
||||
concatMuxDelay: "0",
|
||||
logFfmpeg: false,
|
||||
@ -765,6 +769,19 @@ function addScalingAlgorithm(db) {
|
||||
fs.writeFileSync( f, JSON.stringify( [ffmpegSettings] ) );
|
||||
}
|
||||
|
||||
function addFFMpegLock(db) {
|
||||
let ffmpegSettings = db['ffmpeg-settings'].find()[0];
|
||||
let f = path.join(process.env.DATABASE, 'ffmpeg-settings.json');
|
||||
if ( typeof(ffmpegSettings.ffmpegPathLockDate) === 'undefined' || ffmpegSettings.ffmpegPathLockDate == null ) {
|
||||
|
||||
console.log("Adding ffmpeg lock. For your security it will not be possible to modify the ffmpeg path using the UI anymore unless you launch dizquetv by following special instructions..");
|
||||
// We are migrating an existing db that had a ffmpeg path. Make sure
|
||||
// it's already locked.
|
||||
ffmpegSettings.ffmpegPathLockDate = new Date().getTime() - 2 * DAY_MS;
|
||||
}
|
||||
fs.writeFileSync( f, JSON.stringify( [ffmpegSettings] ) );
|
||||
}
|
||||
|
||||
function moveBackup(path) {
|
||||
if (fs.existsSync(`${process.env.DATABASE}${path}`) ) {
|
||||
let i = 0;
|
||||
|
||||
@ -18,7 +18,7 @@ class FFMPEGInfo {
|
||||
var m = s.match( /version\s+([^\s]+)\s+.*Copyright/ )
|
||||
if (m == null) {
|
||||
console.error("ffmpeg -version command output not in the expected format: " + s);
|
||||
return s;
|
||||
return "Unknown";
|
||||
}
|
||||
return m[1];
|
||||
} catch (err) {
|
||||
|
||||
@ -495,9 +495,17 @@ class FFMPEG extends events.EventEmitter {
|
||||
if ( transcodeVideo && (this.audioOnly !== true) ) {
|
||||
// add the video encoder flags
|
||||
ffmpegArgs.push(
|
||||
'-crf', '22',
|
||||
`-maxrate:v`, `${this.opts.videoBitrate}k`,
|
||||
`-bufsize:v`, `${this.opts.videoBufSize}k`
|
||||
);
|
||||
if (this.opts.videoEncoder.toLowerCase() === "mpeg2video") {
|
||||
// This makes message "impossible bitrate constraints, this will fail" appear but nothing actually fails and it really looks like b:v is the only way to make the video look good when using mpeg2video
|
||||
ffmpegArgs.push(
|
||||
`-qscale:v`, `1`,
|
||||
'-b:v', `${this.opts.videoBitrate}k`
|
||||
);
|
||||
}
|
||||
}
|
||||
if ( transcodeAudio ) {
|
||||
// add the audio encoder flags
|
||||
|
||||
123
src/services/ffmpeg-settings-service.js
Normal file
123
src/services/ffmpeg-settings-service.js
Normal file
@ -0,0 +1,123 @@
|
||||
const databaseMigration = require('../database-migration');
|
||||
const DAY_MS = 1000 * 60 * 60 * 24;
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
class FfmpegSettingsService {
|
||||
constructor(db, unlock) {
|
||||
this.db = db;
|
||||
if (unlock) {
|
||||
this.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
get() {
|
||||
let ffmpeg = this.getCurrentState();
|
||||
if (isLocked(ffmpeg)) {
|
||||
ffmpeg.lock = true;
|
||||
}
|
||||
// Hid this info from the API
|
||||
delete ffmpeg.ffmpegPathLockDate;
|
||||
return ffmpeg;
|
||||
}
|
||||
|
||||
unlock() {
|
||||
let ffmpeg = this.getCurrentState();
|
||||
console.log("ffmpeg path UI unlocked for another day...");
|
||||
ffmpeg.ffmpegPathLockDate = new Date().getTime() + DAY_MS;
|
||||
this.db['ffmpeg-settings'].update({ _id: ffmpeg._id }, ffmpeg)
|
||||
}
|
||||
|
||||
|
||||
update(attempt) {
|
||||
let ffmpeg = this.getCurrentState();
|
||||
attempt.ffmpegPathLockDate = ffmpeg.ffmpegPathLockDate;
|
||||
if (isLocked(ffmpeg)) {
|
||||
console.log("Note: ffmpeg path is not being updated since it's been locked for your security.");
|
||||
attempt.ffmpegPath = ffmpeg.ffmpegPath;
|
||||
if (typeof(ffmpeg.ffmpegPathLockDate) === 'undefined') {
|
||||
// make sure to lock it even if it was undefined
|
||||
attempt.ffmpegPathLockDate = new Date().getTime() - DAY_MS;
|
||||
}
|
||||
} else if (attempt.addLock === true) {
|
||||
// lock it right now
|
||||
attempt.ffmpegPathLockDate = new Date().getTime() - DAY_MS;
|
||||
} else {
|
||||
attempt.ffmpegPathLockDate = new Date().getTime() + DAY_MS;
|
||||
}
|
||||
delete attempt.addLock;
|
||||
delete attempt.lock;
|
||||
|
||||
let err = fixupFFMPEGSettings(attempt);
|
||||
if ( typeof(err) !== "undefined" ) {
|
||||
return {
|
||||
error: err
|
||||
}
|
||||
}
|
||||
|
||||
this.db['ffmpeg-settings'].update({ _id: ffmpeg._id }, attempt)
|
||||
return {
|
||||
ffmpeg: this.get()
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
// Even if reseting, it's impossible to unlock the ffmpeg path
|
||||
let ffmpeg = databaseMigration.defaultFFMPEG() ;
|
||||
this.update(ffmpeg);
|
||||
return this.get();
|
||||
}
|
||||
|
||||
getCurrentState() {
|
||||
return this.db['ffmpeg-settings'].find()[0]
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
function fixupFFMPEGSettings(ffmpeg) {
|
||||
if (typeof(ffmpeg.ffmpegPath) !== 'string') {
|
||||
return "ffmpeg path is required."
|
||||
}
|
||||
if (! isValidFilePath(ffmpeg.ffmpegPath)) {
|
||||
return "ffmpeg path must be a valid file path."
|
||||
}
|
||||
|
||||
if (typeof(ffmpeg.maxFPS) === 'undefined') {
|
||||
ffmpeg.maxFPS = 60;
|
||||
return null;
|
||||
} else if ( isNaN(ffmpeg.maxFPS) ) {
|
||||
return "maxFPS should be a number";
|
||||
}
|
||||
}
|
||||
|
||||
//These checks are good but might not be enough, as long as we are letting the
|
||||
//user choose any path and we are making dizqueTV execute, it is too risky,
|
||||
//hence why we are also adding the lock feature on top of these checks.
|
||||
function isValidFilePath(filePath) {
|
||||
const normalizedPath = path.normalize(filePath);
|
||||
|
||||
if (!path.isAbsolute(normalizedPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const stats = fs.statSync(normalizedPath);
|
||||
return stats.isFile();
|
||||
} catch (err) {
|
||||
// Handle potential errors (e.g., file not found, permission issues)
|
||||
if (err.code === 'ENOENT') {
|
||||
return false; // File does not exist
|
||||
} else {
|
||||
throw err; // Re-throw other errors for debugging
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isLocked(ffmpeg) {
|
||||
return isNaN(ffmpeg.ffmpegPathLockDate) || ffmpeg.ffmpegPathLockDate < new Date().getTime();
|
||||
}
|
||||
|
||||
|
||||
|
||||
module.exports =FfmpegSettingsService;
|
||||
@ -562,6 +562,9 @@ function makeEntry(channel, x) {
|
||||
episode: x.program.episode,
|
||||
title: x.program.title,
|
||||
}
|
||||
} else if (x.program.type === 'track') {
|
||||
title = x.program.title;
|
||||
// TODO: Add sub data for tracks here for XML writing
|
||||
}
|
||||
}
|
||||
if (typeof(title)==='undefined') {
|
||||
|
||||
@ -183,6 +183,11 @@ function video( channelService, fillerDB, db, programmingService, activeChannelS
|
||||
return
|
||||
}
|
||||
|
||||
if (ffmpegSettings.disablePreludes === true) {
|
||||
//disable the preludes
|
||||
isBetween = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -98,6 +98,7 @@ async function _writeProgramme(channel, program, xw, xmlSettings, cacheImageServ
|
||||
xw.writeRaw('\n <previously-shown/>')
|
||||
|
||||
//sub-title
|
||||
// TODO: Add support for track data (artist, album) here
|
||||
if ( typeof(program.sub) !== 'undefined') {
|
||||
xw.startElement('sub-title')
|
||||
xw.writeAttribute('lang', 'en')
|
||||
|
||||
@ -312,7 +312,7 @@ module.exports = function ($scope, $timeout, dizquetv) {
|
||||
ch.programs.push( {
|
||||
duration: addDuration(b - a),
|
||||
altTitle: altTitle,
|
||||
showTitle: program.title,
|
||||
showTitle: program.title, // movie title, episode title or track title
|
||||
subTitle: subTitle,
|
||||
episodeTitle : episodeTitle,
|
||||
start: hasStart,
|
||||
|
||||
@ -11,8 +11,13 @@ module.exports = function (dizquetv, resolutionOptions) {
|
||||
scope.settings = settings
|
||||
})
|
||||
scope.updateSettings = (settings) => {
|
||||
delete scope.settingsError;
|
||||
dizquetv.updateFfmpegSettings(settings).then((_settings) => {
|
||||
scope.settings = _settings
|
||||
}).catch( (err) => {
|
||||
if ( typeof(err.data) === "string") {
|
||||
scope.settingsError = err.data;
|
||||
}
|
||||
})
|
||||
}
|
||||
scope.resetSettings = (settings) => {
|
||||
|
||||
@ -1,4 +1,8 @@
|
||||
<div>
|
||||
<small class="text-danger" nf-show="settingsError">
|
||||
{{ settingsError }}
|
||||
</small>
|
||||
|
||||
<h5>FFMPEG Settings
|
||||
|
||||
<button class="pull-right btn btn-sm btn-success" style="margin-left: 5px;" ng-click="updateSettings(settings)">
|
||||
@ -8,9 +12,48 @@
|
||||
Reset Options
|
||||
</button>
|
||||
</h5>
|
||||
<h6>FFMPEG Executable Path (eg: C:\ffmpeg\bin\ffmpeg.exe || /usr/bin/ffmpeg)</h6>
|
||||
<input type="text" class="form-control form-control-sm" ria-describedby="ffmpegHelp" ng-model="settings.ffmpegPath"></input>
|
||||
<small id="ffmpegHelp" class="form-text text-muted">FFMPEG version 4.2+ required. Check by opening the version tab</small>
|
||||
|
||||
<hr></hr>
|
||||
<h6>FFMPEG Executable Path</h6>
|
||||
<div class="row" ng-show="settings.lock !== true">
|
||||
<div class="col-sm-9">
|
||||
<div class="form-group">
|
||||
<div class="form-group">
|
||||
<label>Path</label>
|
||||
<input id="ffmpegPath" ria-describedby="ffmpegHelp" type="text" class="form-control form-control-sm" ng-model="settings.ffmpegPath"></input>
|
||||
<small class="form-text text-muted">
|
||||
The path to the ffmpeg executable. (e.g: /usr/bin/ffmpeg or C:\ffmpeg\bin\ffmpeg.exe) FFMPEG version 4.2+ required. Check by opening the version tab.
|
||||
|
||||
</small>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-9">
|
||||
<div class="form-group">
|
||||
<input id="lockFfmpeg" type="checkbox" ng-model="settings.addLock"></input>
|
||||
<label for="lockFfmpeg">Lock ffmpeg path setting</label>
|
||||
<small class="form-text text-muted">This will lock the ffmpeg path setting so that it is no longer editable from UI. Even if you don't toggle this option, the setting will get locked in 24 hours.</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row" ng-show="settings.lock === true">
|
||||
<div class="col-sm-9">
|
||||
<div class="form-group">
|
||||
<div class="form-group">
|
||||
<label>Path</label>
|
||||
<input id="ffmpegPath" ria-describedby="ffmpegHelp" type="text" class="form-control form-control-sm" ng-model="settings.ffmpegPath" readonly></input>
|
||||
<small class="form-text text-muted">
|
||||
The ffmpeg path setting is currently locked and can't be edited from the UI. It's not usually necessary to update this path once it's known to be working. Run dizquetv with the <b>--unlock</b> command line argument to enable editing it again.
|
||||
</small>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr></hr>
|
||||
<h6>Miscellaneous Options</h6>
|
||||
<div class="row">
|
||||
@ -196,6 +239,18 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br ></br>
|
||||
<div class="row">
|
||||
<div class="col-sm-9">
|
||||
<div class="form-group">
|
||||
<input id="disablePreludes" type="checkbox" ng-model="settings.disablePreludes" ng-disabled="isTranscodingNotNeeded()" ></input>
|
||||
<label for="disablePreludes">Disable Preludes</label>
|
||||
<small class="form-text text-muted">In an attempt to improve playback, dizqueTV insets really short clips of black screen between videos. The idea is that if the stream pauses because Plex is taking too long to reply, it will pause during one of those black screens instead of interrupting the last second of a video. If you suspect these black screens are causing trouble instead of helping, you can disable them with this option.
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@ -65,7 +65,7 @@
|
||||
</div>
|
||||
<div class='form-group col-md-7' ng-if="schedule.randomDistribution == 'weighted'" >
|
||||
<label ng-if="$index==0" for="weightRange{{$index}}">Weight</label>
|
||||
<input class='form-control-range custom-range' id="weightRange{{$index}}" type='range' ng-model='slot.weight' min=1 max=600
|
||||
<input class='form-control-range custom-range' id="weightRange{{$index}}" type='range' ng-model='slot.weight' min=1 max=60000
|
||||
data-toggle="tooltip" data-placement="bottom" title="Slots with more weight will be picked more frequently."
|
||||
ng-change="refreshSlots()"
|
||||
>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user