1
mirror of https://github.com/streamlink/streamlink synced 2024-11-16 05:03:49 +01:00

Refactor stream input/output.

- Now uses pbs library to handle subprocesses.
- Removed -c option as not all plugins may be based on subprocesses.
This commit is contained in:
Christopher Rosell 2012-05-25 17:26:11 +02:00
parent e4c07414d0
commit 3957b734ed
10 changed files with 110 additions and 81 deletions

View File

@ -59,14 +59,14 @@ Livestreamer is also a library. Short example:
streams = channel.get_streams()
stream = streams["720p"]
stream.open()
fd = stream.open()
while True:
data = stream.read(1024)
data = fd.read(1024)
if len(data) == 0:
break
# do something with data
stream.close()
fd.close()

View File

@ -4,7 +4,7 @@ from setuptools import setup, find_packages
from sys import version_info
version = "1.0.0"
deps = []
deps = ["pbs"]
# require argparse on Python <2.7 and <3.2
if (version_info[0] == 2 and version_info[1] < 7) or \

View File

@ -1,4 +1,5 @@
from livestreamer import plugins
from livestreamer import stream
from livestreamer.compat import urlparse
def resolve_url(url):
@ -21,5 +22,6 @@ def get_plugins():
PluginError = plugins.PluginError
NoStreamsError = plugins.NoStreamsError
NoPluginError = plugins.NoPluginError
StreamError = stream.StreamError
plugins.load_plugins(plugins)

View File

@ -1,7 +1,8 @@
#!/usr/bin/env python3
import sys, os
import sys, os, pbs
import livestreamer
from livestreamer.compat import input
parser = livestreamer.utils.ArgumentParser(description="Util to play various livestreaming services in a custom player",
fromfile_prefix_chars="@")
@ -9,7 +10,6 @@ parser.add_argument("url", help="URL to stream", nargs="?")
parser.add_argument("stream", help="stream to play", nargs="?")
parser.add_argument("-p", "--player", metavar="player", help="commandline for player", default="vlc")
parser.add_argument("-o", "--output", metavar="filename", help="write stream to file instead of playing it")
parser.add_argument("-c", "--cmdline", action="store_true", help="print commandline used internally to play stream")
parser.add_argument("-l", "--plugins", action="store_true", help="print installed plugins")
RCFILE = os.path.expanduser("~/.livestreamerrc")
@ -18,6 +18,33 @@ def exit(msg):
sys.stderr.write("error: " + msg + "\n")
sys.exit()
def msg(msg):
sys.stderr.write(msg + "\n")
def write_stream(fd, out, progress):
written = 0
while True:
data = fd.read(8192)
if len(data) == 0:
break
try:
out.write(data)
except IOError:
break
written += len(data)
if progress:
sys.stdout.write(("\rWritten {0} bytes").format(written))
if progress and written > 0:
sys.stdout.write("\n")
fd.close()
out.close()
def handle_url(args):
try:
channel = livestreamer.resolve_url(args.url)
@ -39,26 +66,47 @@ def handle_url(args):
if args.stream:
if args.stream in streams:
stream = streams[args.stream]
cmdline = stream.cmdline(args.output or "-")
if args.cmdline:
print(cmdline.format())
sys.exit()
try:
fd = stream.open()
except livestreamer.StreamError as err:
exit(("Could not open stream - {0}").format(err))
progress = False
if args.output:
progress = True
if os.path.exists(args.output):
answer = input(("File output {0} already exists! Overwrite it? [y/N] ").format(args.output))
answer = answer.strip().lower()
if answer != "y":
sys.exit()
try:
out = open(args.output, "wb")
except IOError as err:
exit(("Failed to open file {0} - ").format(args.output, err))
else:
if not args.output:
cmdline.pipe = ("{0} -").format(args.player)
cmd = args.player + " -"
player = pbs.sh("-c", cmd, _bg=True, _out=sys.stdout, _err=sys.stderr)
out = player.process.stdin
os.system(cmdline.format())
try:
write_stream(fd, out, progress)
except KeyboardInterrupt:
sys.exit()
else:
print(("This channel does not have stream: {0}").format(args.stream))
print(("Valid streams: {0}").format(validstreams))
msg(("This channel does not have stream: {0}").format(args.stream))
msg(("Valid streams: {0}").format(validstreams))
else:
print(("Found streams: {0}").format(validstreams))
msg(("Found streams: {0}").format(validstreams))
def print_plugins():
pluginlist = list(livestreamer.get_plugins().keys())
print(("Installed plugins: {0}").format(", ".join(pluginlist)))
msg(("Installed plugins: {0}").format(", ".join(pluginlist)))
def main():

View File

@ -7,6 +7,7 @@ is_py3 = (sys.version_info[0] == 3)
if is_py2:
str = unicode
input = raw_input
def bytes(b, enc="ascii"):
return str(b)
@ -14,6 +15,7 @@ if is_py2:
elif is_py3:
str = str
bytes = bytes
input = input
try:
import urllib.request as urllib

View File

@ -110,7 +110,7 @@ class JustinTV(Plugin):
"swfUrl": self.SWFURL,
"swfhash": swfhash,
"swfsize": swfsize,
"live": 1
"live": True
})
if "token" in info:

View File

@ -82,7 +82,7 @@ class OwnedTV(Plugin):
if not name in streams:
streams[name] = RTMPStream({
"rtmp": ("{0}/{1}").format(base, playpath),
"live": 1
"live": True
})
return streams

View File

@ -46,7 +46,7 @@ class UStreamTV(Plugin):
"rtmp": ("{0}/{1}").format(cdnurl or fmsurl, playpath),
"pageUrl": self.url,
"swfUrl": self.SWFURL,
"live": 1
"live": True
})
streams["live"] = stream

View File

@ -1,45 +1,35 @@
from livestreamer.utils import CommandLine
from livestreamer.utils import urlopen
import subprocess, shlex
import pbs
class StreamError(Exception):
pass
class Stream(object):
def __init__(self, params={}):
self.params = params
self.process = None
def open(self):
if self.process:
self.close()
cmdline = self.cmdline().format()
args = shlex.split(cmdline)
self.process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
def read(self, *args):
if self.process:
return self.process.stdout.read(*args)
def close(self):
if self.process:
self.process.kill()
self.process = None
def cmdline(self, out=None):
raise NotImplementedError
class RTMPStream(Stream):
def cmdline(self, out=None):
cmd = CommandLine("rtmpdump")
def __init__(self, params):
self.params = params or {}
for key, value in self.params.items():
if key == "live":
if value == 1:
cmd.args[key] = True
def open(self):
try:
rtmpdump = pbs.rtmpdump
except pbs.CommandNotFound:
raise StreamError("Unable to find 'rtmpdump' command")
cmd.args[key] = value
self.params["flv"] = "-"
self.params["_bg"] = True
if out:
cmd.args["flv"] = out
stream = rtmpdump(**self.params)
return stream.process.stdout
class HTTPStream(Stream):
def __init__(self, url):
self.url = url
def open(self):
return urlopen(self.url)
return cmd

View File

@ -1,35 +1,11 @@
#!/usr/bin/env python3
from livestreamer.compat import urllib, bytes
from livestreamer.compat import urllib
from livestreamer.plugins import PluginError
import hmac, hashlib, zlib, argparse
SWF_KEY = b"Genuine Adobe Flash Player 001"
class CommandLine(object):
def __init__(self, command):
self.command = command
self.args = {}
self.pipe = None
def format(self):
args = []
for key, value in self.args.items():
if value == True:
args.append(("--{0}").format(key))
else:
escaped = str(value).replace('"', '\\"').replace("$", "\$").replace("`", "\`")
args.append(("--{0} \"{1}\"").format(key, escaped))
args = (" ").join(args)
cmdline = ("{0} {1}").format(self.command, args)
if self.pipe:
cmdline += (" | {0}").format(self.pipe)
return cmdline
class ArgumentParser(argparse.ArgumentParser):
def convert_arg_line_to_args(self, line):
split = line.find("=")
@ -37,16 +13,27 @@ class ArgumentParser(argparse.ArgumentParser):
val = line[split+1:].strip()
yield "--%s=%s" % (key, val)
def urlget(url, data=None, timeout=None, opener=None):
def urlopen(url, data=None, timeout=None, opener=None):
try:
if opener is not None:
fd = opener.open(url)
else:
fd = urllib.urlopen(url, data, timeout)
except IOError as err:
if type(err) is urllib.URLError:
raise PluginError(err.reason)
else:
raise PluginError(err)
return fd
def urlget(url, data=None, timeout=None, opener=None):
fd = urlopen(url, data, timeout, opener)
try:
data = fd.read()
fd.close()
except IOError as err:
if type(err) is urllib.URLError:
raise PluginError(err.reason)