This commit is contained in:
deathbybandaid 2021-02-04 09:19:50 -05:00
parent 5e0958df2b
commit 107e2d88c2
12 changed files with 281 additions and 1 deletions

13
LICENSE Normal file
View File

@ -0,0 +1,13 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2017 Sam Zick <Sam@deathbybandaid.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.

View File

@ -1,2 +1,22 @@
# fHDHR_plugin_web_watch <p align="center">fHDHR web Plugin Watch <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:
**f**un
**H**ome
**D**istribution
**H**iatus
**R**ecreation
fHDHR is labeled as beta until we reach v1.0.0
Join us in `#fHDHR <irc://irc.freenode.net/#fHDHR>`_ on Freenode.
# Installation
1) Review Installation guide located at [Docs](https://github.com/fHDHR/fHDHR/blob/main/docs/README.md)
2) Insert this plugin into the `plugins` directory of fHDHR using `git clone` or downloading a release zip file.
3) Adjust your configuration file with the below settings:

11
__init__.py Normal file
View File

@ -0,0 +1,11 @@
from .plutotv_html import PlutoTV_HTML
class Plugin_OBJ():
def __init__(self, fhdhr, plugin_utils):
self.fhdhr = fhdhr
self.plugin_utils = plugin_utils
self.plutotv_html = PlutoTV_HTML(fhdhr, plugin_utils)

5
plugin.json Normal file
View File

@ -0,0 +1,5 @@
{
"name":"webwatch",
"version":"v0.6.0-beta",
"type":"origin"
}

0
requirements.txt Normal file
View File

168
stream/__init__.py Normal file
View File

@ -0,0 +1,168 @@
import os
import sys
import subprocess
from fHDHR.exceptions import TunerError
def setup(plugin):
# Check config for ffmpeg path
ffmpeg_path = None
if plugin.config.dict["webwatch"]["ffmpeg_path"]:
# verify path is valid
if os.path.isfile(plugin.config.dict["webwatch"]["ffmpeg_path"]):
ffmpeg_path = plugin.config.dict["webwatch"]["ffmpeg_path"]
else:
plugin.logger.warning("Failed to find ffmpeg at %s." % plugin.config.dict["webwatch"]["ffmpeg_path"])
if not ffmpeg_path:
plugin.logger.info("Attempting to find ffmpeg in PATH.")
if plugin.config.internal["versions"]["Operating System"]["version"] in ["Linux", "Darwin"]:
find_ffmpeg_command = ["which", "ffmpeg"]
elif plugin.config.internal["versions"]["Operating System"]["version"] in ["Windows"]:
find_ffmpeg_command = ["where", "ffmpeg"]
ffmpeg_proc = subprocess.Popen(find_ffmpeg_command, stdout=subprocess.PIPE)
ffmpeg_path = ffmpeg_proc.stdout.read().decode().strip("\n")
ffmpeg_proc.terminate()
ffmpeg_proc.communicate()
ffmpeg_proc.kill()
if not ffmpeg_path:
ffmpeg_path = None
elif ffmpeg_path.isspace():
ffmpeg_path = None
if ffmpeg_path:
plugin.config.dict["webwatch"]["ffmpeg_path"] = ffmpeg_path
if ffmpeg_path:
ffmpeg_command = [ffmpeg_path, "-version", "pipe:stdout"]
try:
ffmpeg_proc = subprocess.Popen(ffmpeg_command, stdout=subprocess.PIPE)
ffmpeg_version = ffmpeg_proc.stdout.read().decode().split("version ")[1].split(" ")[0]
except FileNotFoundError:
ffmpeg_version = None
except PermissionError:
ffmpeg_version = None
finally:
ffmpeg_proc.terminate()
ffmpeg_proc.communicate()
ffmpeg_proc.kill()
if not ffmpeg_version:
ffmpeg_version = "Missing"
plugin.logger.warning("Failed to find ffmpeg.")
plugin.config.register_version("ffmpeg", ffmpeg_version, "env")
class Plugin_OBJ():
def __init__(self, fhdhr, plugin_utils, stream_args, tuner):
self.fhdhr = fhdhr
self.plugin_utils = plugin_utils
self.stream_args = stream_args
self.tuner = tuner
if self.plugin_utils.config.internal["versions"]["ffmpeg"]["version"] == "Missing":
raise TunerError("806 - Tune Failed: FFMPEG Missing")
self.bytes_per_read = int(plugin_utils.config.dict["streaming"]["bytes_per_read"])
self.ffmpeg_command = self.ffmpeg_command_assemble(stream_args)
def get(self):
ffmpeg_proc = subprocess.Popen(self.ffmpeg_command, stdout=subprocess.PIPE)
def generate():
try:
while self.tuner.tuner_lock.locked():
chunk = ffmpeg_proc.stdout.read(self.bytes_per_read)
if not chunk:
break
# raise TunerError("807 - No Video Data")
yield chunk
chunk_size = int(sys.getsizeof(chunk))
self.tuner.add_downloaded_size(chunk_size)
self.plugin_utils.logger.info("Connection Closed: Tuner Lock Removed")
except GeneratorExit:
self.plugin_utils.logger.info("Connection Closed.")
except Exception as e:
self.plugin_utils.logger.info("Connection Closed: %s" % e)
finally:
ffmpeg_proc.terminate()
ffmpeg_proc.communicate()
ffmpeg_proc.kill()
self.plugin_utils.logger.info("Connection Closed: Tuner Lock Removed")
if hasattr(self.fhdhr.origins.origins_dict[self.tuner.origin], "close_stream"):
self.fhdhr.origins.origins_dict[self.tuner.origin].close_stream(self.tuner.number, self.stream_args)
self.tuner.close()
# raise TunerError("806 - Tune Failed")
return generate()
def ffmpeg_command_assemble(self, stream_args):
ffmpeg_command = [
self.plugin_utils.config.dict["webwatch"]["ffmpeg_path"],
"-i", stream_args["stream_info"]["url"],
]
ffmpeg_command.extend(self.ffmpeg_headers(stream_args))
ffmpeg_command.extend(self.ffmpeg_duration(stream_args))
ffmpeg_command.extend(self.transcode_profiles(stream_args))
ffmpeg_command.extend(self.ffmpeg_loglevel())
ffmpeg_command.extend(["pipe:stdout"])
return ffmpeg_command
def ffmpeg_headers(self, stream_args):
ffmpeg_command = []
if stream_args["stream_info"]["headers"]:
headers_string = ""
if len(list(stream_args["stream_info"]["headers"].keys())) > 1:
for x in list(stream_args["stream_info"]["headers"].keys()):
headers_string += "%s: %s\r\n" % (x, stream_args["stream_info"]["headers"][x])
else:
for x in list(stream_args["stream_info"]["headers"].keys()):
headers_string += "%s: %s" % (x, stream_args["stream_info"]["headers"][x])
ffmpeg_command.extend(["-headers", '\"%s\"' % headers_string])
return ffmpeg_command
def ffmpeg_duration(self, stream_args):
ffmpeg_command = []
if stream_args["duration"]:
ffmpeg_command.extend(["-t", str(stream_args["duration"])])
else:
ffmpeg_command.extend(
[
"-reconnect", "1",
"-reconnect_at_eof", "1",
"-reconnect_streamed", "1",
"-reconnect_delay_max", "2",
]
)
return ffmpeg_command
def ffmpeg_loglevel(self):
ffmpeg_command = []
log_level = self.plugin_utils.config.dict["logging"]["level"].lower()
loglevel_dict = {
"debug": "debug",
"info": "info",
"error": "error",
"warning": "warning",
"critical": "fatal",
}
if log_level not in ["info", "debug"]:
ffmpeg_command.extend(["-nostats", "-hide_banner"])
ffmpeg_command.extend(["-loglevel", loglevel_dict[log_level]])
return ffmpeg_command
def transcode_profiles(self, stream_args):
ffmpeg_command = ["-vcodec", "libx264", "-pix_fmt", "yuv420p"]
return ffmpeg_command

3
stream/plugin.json Normal file
View File

@ -0,0 +1,3 @@
{
"type":"alt_stream"
}

11
web/__init__.py Normal file
View File

@ -0,0 +1,11 @@
from .webwatch_html import Watch_HTML
class Plugin_OBJ():
def __init__(self, fhdhr, plugin_utils):
self.fhdhr = fhdhr
self.plugin_utils = plugin_utils
self.webwatch_html = Watch_HTML(fhdhr, plugin_utils)

3
web/plugin.json Normal file
View File

@ -0,0 +1,3 @@
{
"type":"web"
}

11
web/webwatch.html Normal file
View File

@ -0,0 +1,11 @@
{% extends "base.html" %}
{% block content %}
<h4 style="text-align: center;">fHDHR Channel Watch</h4>
<video width='50%' height='40%' autoplay controls>
<source src="{{ watch_url }}" type="video/mp4"> Your browser does not support the video tag.
</video>
{% endblock %}

26
web/webwatch_html.py Normal file
View File

@ -0,0 +1,26 @@
from flask import request, render_template, session
class Watch_HTML():
endpoints = ["/webwatch", "/webwatch.html"]
endpoint_name = "page_webwatch_html"
endpoint_access_level = 0
pretty_name = "Watch"
endpoint_category = "pages"
def __init__(self, fhdhr, plugin_utils):
self.fhdhr = fhdhr
def __call__(self, *args):
return self.get(*args)
def get(self, *args):
base_url = request.url_root[:-1]
origin = self.fhdhr.origins.valid_origins[0]
channel_id = [x["id"] for x in self.fhdhr.device.channels.get_channels(origin)][0]
watch_url = '%s/api/tuners?method=stream&channel=%s&origin=%s' % (base_url, channel_id, origin)
return render_template('webwatch.html', request=request, session=session, fhdhr=self.fhdhr, watch_url=watch_url)

9
webwatch_conf.json Normal file
View File

@ -0,0 +1,9 @@
{
"webwatch":{
"ffmpeg_path":{
"value": "none",
"config_file": true,
"config_web": true
}
}
}