Skip to content

Commit

Permalink
improve command line scripts
Browse files Browse the repository at this point in the history
ini file pkg

better default .ini package

include logo in package

better placement of package data_files

more robust package data_files finding

mypy
  • Loading branch information
scivision committed May 30, 2019
1 parent ba272fa commit 6911cee
Show file tree
Hide file tree
Showing 19 changed files with 121 additions and 108 deletions.
9 changes: 3 additions & 6 deletions Glob.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,7 @@ def main():
help='site to stream: [youtube,periscope,facebook,twitch]',
nargs='?', default='localhost')
p.add_argument('-glob', help='file glob pattern to stream.')
p.add_argument('-i', '--ini', help='*.ini file with stream parameters',
default='stream.ini')
p.add_argument('-i', '--ini', help='*.ini file with stream parameters')
p.add_argument('-image',
help='static image to display, for audio-only files.')
p.add_argument('-shuffle', help='shuffle the globbed file list',
Expand All @@ -96,15 +95,13 @@ def main():
else:
input(f"Press Enter to go live on {site}. Or Ctrl C to abort.")

inifn = Path(P.ini).expanduser()

usemeta = P.nometa

if P.loop:
while True:
playonce(flist, P.image, site, inifn, P.shuffle, usemeta, P.yes)
playonce(flist, P.image, site, P.ini, P.shuffle, usemeta, P.yes)
else:
playonce(flist, P.image, site, inifn, P.shuffle, usemeta, P.yes)
playonce(flist, P.image, site, P.ini, P.shuffle, usemeta, P.yes)


if __name__ == '__main__':
Expand Down
6 changes: 1 addition & 5 deletions Loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,10 @@
https://support.google.com/youtube/answer/2853702
"""
from typing import List
from pathlib import Path
import pylivestream as pls
import signal
from argparse import ArgumentParser

R = Path(__file__).parent


def main():
signal.signal(signal.SIGINT, signal.SIG_DFL)
Expand All @@ -25,8 +22,7 @@ def main():
p.add_argument('site',
help='site to stream: [youtube,periscope,facebook,twitch]',
nargs='?', default='localhost')
p.add_argument('-i', '--ini', help='*.ini file with stream parameters',
default=R/'stream.ini')
p.add_argument('-i', '--ini', help='*.ini file with stream parameters')
p.add_argument('-y', '--yes', help='no confirmation dialog', action='store_true')
p.add_argument('-t', '--timeout', help='stop streaming after --timeout seconds', type=int)
P = p.parse_args()
Expand Down
3 changes: 0 additions & 3 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,5 +1,2 @@
include doc/logo.png
include LICENSE
include *.py
recursive-include tests *.py *.ogv *.avi *.gif *.png
include pytest.ini mypy.ini .flake8
13 changes: 3 additions & 10 deletions Microphone.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,19 @@
ffmpeg -list_devices true -f dshow -i dummy
"""
from pathlib import Path
import pylivestream as pls
import signal
from argparse import ArgumentParser

R = Path(__file__).parent


def main():
signal.signal(signal.SIGINT, signal.SIG_DFL)

p = ArgumentParser(description="livestream microphone audio")
p.add_argument('site',
help='site to stream: [youtube,periscope,facebook,twitch]',
p.add_argument('site', help='site to stream: [youtube,periscope,facebook,twitch]',
nargs='?', default='localhost')
p.add_argument('-image', help='static image to display.',
default=R/'doc/logo.png')
p.add_argument('-i', '--ini',
help='*.ini file with stream parameters',
default=R/'stream.ini')
p.add_argument('-image', help='static image to display.')
p.add_argument('-i', '--ini', help='*.ini file with stream parameters')
p.add_argument('-y', '--yes', help='no confirmation dialog', action='store_true')
p.add_argument('-t', '--timeout', help='stop streaming after --timeout seconds', type=int)
P = p.parse_args()
Expand Down
3 changes: 1 addition & 2 deletions ScreenCapture2disk.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ def main():

p = ArgumentParser()
p.add_argument('outfn', help='video file to save to disk.')
p.add_argument('-i', '--ini', help='*.ini file with stream parameters',
default='stream.ini')
p.add_argument('-i', '--ini', help='*.ini file with stream parameters')
p.add_argument('-y', '--yes', help='no confirmation dialog', action='store_true')
p.add_argument('-t', '--timeout', help='stop streaming after --timeout seconds', type=int)
P = p.parse_args()
Expand Down
6 changes: 1 addition & 5 deletions Screenshare.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,10 @@
ffmpeg -list_devices true -f dshow -i dummy
"""
from typing import List
from pathlib import Path
import pylivestream as pls
import signal
from argparse import ArgumentParser

R = Path(__file__).parent


def main():
signal.signal(signal.SIGINT, signal.SIG_DFL)
Expand All @@ -24,8 +21,7 @@ def main():
p.add_argument('site',
help='site to stream: [youtube,periscope,facebook,twitch]',
nargs='?', default='localhost')
p.add_argument('-i', '--ini', help='*.ini file with stream parameters',
default=R/'stream.ini')
p.add_argument('-i', '--ini', help='*.ini file with stream parameters')
p.add_argument('-y', '--yes', help='no confirmation dialog', action='store_true')
p.add_argument('-t', '--timeout', help='stop streaming after --timeout seconds', type=int)
P = p.parse_args()
Expand Down
6 changes: 1 addition & 5 deletions Webcam.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,18 @@
ffmpeg -list_devices true -f dshow -i dummy
"""
from typing import List
from pathlib import Path
import pylivestream as pls
import signal
from argparse import ArgumentParser

R = Path(__file__).parent


def main():
signal.signal(signal.SIGINT, signal.SIG_DFL)

p = ArgumentParser(description="livestream webcam")
p.add_argument('site', help='site to stream: [youtube,periscope,facebook,twitch]',
nargs='?', default='localhost')
p.add_argument('-i', '--ini', help='*.ini file with stream parameters',
default=R/'stream.ini')
p.add_argument('-i', '--ini', help='*.ini file with stream parameters')
p.add_argument('-y', '--yes', help='no confirmation dialog',
action='store_true')
p.add_argument('-t', '--timeout', help='stop streaming after --timeout seconds', type=int)
Expand Down
40 changes: 20 additions & 20 deletions pylivestream/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@
class Livestream(stream.Stream):

def __init__(self,
ini: Path, site: str, *,
inifn: Path, site: str, *,
vidsource: str = None,
image: Path = None,
loop: bool = False,
infn: Path = None,
caption: str = None,
yes: bool = False,
timeout: int = None) -> None:
super().__init__(ini, site, vidsource=vidsource,
super().__init__(inifn, site, vidsource=vidsource,
image=image, loop=loop,
infn=infn, caption=caption, yes=yes,
timeout=timeout)
Expand Down Expand Up @@ -135,7 +135,7 @@ def startlive(self, sinks: Sequence[str] = None):
class Screenshare(Livestream):

def __init__(self,
ini: Path,
inifn: Path,
websites: Union[str, Sequence[str]],
caption: str = None,
yes: bool = False,
Expand All @@ -148,7 +148,7 @@ def __init__(self,

streams = {}
for site in websites:
streams[site] = Livestream(ini, site, vidsource=vidsource,
streams[site] = Livestream(inifn, site, vidsource=vidsource,
caption=caption, yes=yes, timeout=timeout)

self.streams: Dict[str, Livestream] = streams
Expand All @@ -167,7 +167,7 @@ def golive(self):
class Webcam(Livestream):

def __init__(self,
ini: Path,
inifn: Path,
websites: Union[str, Sequence[str]], *,
caption: str = None,
yes: bool = False,
Expand All @@ -180,7 +180,7 @@ def __init__(self,

streams = {}
for site in websites:
streams[site] = Livestream(ini, site, vidsource=vidsource,
streams[site] = Livestream(inifn, site, vidsource=vidsource,
caption=caption, yes=yes,
timeout=timeout)

Expand All @@ -200,19 +200,19 @@ def golive(self):
class Microphone(Livestream):

def __init__(self,
ini: Path,
sites: Union[str, Sequence[str]], *,
inifn: Path,
websites: Union[str, Sequence[str]], *,
image: Path,
caption: str = None,
yes: bool = False,
timeout: int = None):

if isinstance(sites, str):
sites = [sites]
if isinstance(websites, str):
websites = [websites]

streams = {}
for site in sites:
streams[site] = Livestream(ini, site, image=image,
for site in websites:
streams[site] = Livestream(inifn, site, image=image,
loop=True,
caption=caption, yes=yes, timeout=timeout)

Expand All @@ -233,8 +233,8 @@ def golive(self):
class FileIn(Livestream):

def __init__(self,
ini: Path,
sites: Union[str, Sequence[str]], *,
inifn: Path,
websites: Union[str, Sequence[str]], *,
infn: Path,
loop: bool = False,
image: Path = None,
Expand All @@ -244,12 +244,12 @@ def __init__(self,

vidsource = 'file'

if isinstance(sites, str):
sites = [sites]
if isinstance(websites, str):
websites = [websites]

streams = {}
for site in sites:
streams[site] = Livestream(ini, site, vidsource=vidsource, image=image,
for site in websites:
streams[site] = Livestream(inifn, site, vidsource=vidsource, image=image,
loop=loop, infn=infn,
caption=caption, yes=yes, timeout=timeout)

Expand All @@ -269,7 +269,7 @@ def golive(self):
class SaveDisk(stream.Stream):

def __init__(self,
ini: Path, outfn: Path = None, *,
inifn: Path, outfn: Path = None, *,
caption: str = None,
yes: bool = False,
timeout: int = None):
Expand All @@ -281,7 +281,7 @@ def __init__(self,
site = 'file'
vidsource = 'screen'

super().__init__(ini, site,
super().__init__(inifn, site,
vidsource=vidsource, caption=caption, timeout=timeout)

self.outfn = Path(outfn).expanduser() if outfn else None
Expand Down
Binary file added pylivestream/data/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes.
50 changes: 34 additions & 16 deletions pylivestream/stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,10 @@
# %% top level
class Stream:

def __init__(self, ini: Path, site: str, *,
def __init__(self, inifn: Path, site: str, *,
vidsource: str = None,
image: Path = None, loop: bool = False, infn: Path = None,
image: Path = None,
loop: bool = False, infn: Path = None,
caption: str = None,
yes: bool = False,
timeout: int = None,
Expand All @@ -56,10 +57,17 @@ def __init__(self, ini: Path, site: str, *,

self.loglevel: List[str] = self.F.INFO if verbose else self.F.ERROR

self.ini: Path = Path(ini).expanduser()
self.inifn: Path = inifn

self.site: str = site
self.vidsource = vidsource
self.image = Path(image).expanduser() if image else None

if image:
self.image = Path(image).expanduser()
else:
# Must be pathlib.Path for detection
self.image = utils.get_pkgfile('data/logo.png')

self.loop: bool = loop

self.infn = Path(infn).expanduser() if infn else None
Expand All @@ -73,13 +81,15 @@ def __init__(self, ini: Path, site: str, *,

def osparam(self):
"""load OS specific config"""
if not self.ini.is_file():
raise FileNotFoundError(self.ini)

C = ConfigParser(inline_comment_prefixes=('#', ';'))
C.read(str(self.ini))
if self.inifn is None:
self.inifn = utils.get_pkgfile('pylivestream.ini')

assert self.site in C, f'{self.site} not found: {self.ini}'
C.read_string(Path(self.inifn).expanduser().read_text(), source=self.inifn)

if self.site not in C:
raise ValueError(f'streaming site {self.site} not found in configuration file {self.inifn}')

if 'XDG_SESSION_TYPE' in os.environ:
if os.environ['XDG_SESSION_TYPE'] == 'wayland':
Expand Down Expand Up @@ -139,7 +149,9 @@ def osparam(self):
C.get(self.site, 'key', fallback=None))

def videoIn(self, quick: bool = False) -> List[str]:
"""config video input"""
"""
config video input
"""

if self.vidsource == 'screen':
v = self.screengrab(quick)
Expand All @@ -156,7 +168,9 @@ def videoIn(self, quick: bool = False) -> List[str]:
return v

def videoOut(self) -> List[str]:
"""configure video output"""
"""
configure video output
"""

vid_format = 'uyvy422' if sys.platform == 'darwin' else 'yuv420p'
v: List[str] = ['-codec:v', 'libx264', '-pix_fmt', vid_format]
Expand All @@ -183,7 +197,7 @@ def videoOut(self) -> List[str]:

def audioIn(self, quick: bool = False) -> List[str]:
"""
-ac * may not be needed, took out.
-ac 2 doesn't seem to be needed, so it was removed.
NOTE: -ac 2 NOT -ac 1 to avoid "non monotonous DTS in output stream" errors
"""
Expand All @@ -200,7 +214,9 @@ def audioIn(self, quick: bool = False) -> List[str]:
return a

def audioOut(self) -> List[str]:
"""select audio codec
"""
select audio codec
https://trac.ffmpeg.org/wiki/Encode/AAC#FAQ
https://support.google.com/youtube/answer/2853702?hl=en
https://www.facebook.com/facebookmedia/get-started/live
Expand All @@ -214,19 +230,21 @@ def audioOut(self) -> List[str]:
'-ar', str(self.audiofs)]

def video_bitrate(self):
"""get "best" video bitrate.
Based on YouTube Live minimum specified stream rate."""
"""
get "best" video bitrate.
Based on YouTube Live minimum specified stream rate.
"""
if self.video_kbps: # per-site override
return

if self.res is not None:
x: int = int(self.res[1])
elif self.vidsource is None or self.vidsource == 'file':
logging.warning('assuming 480p input.')
logging.info('assuming 480p input.')
x = 480
else:
raise ValueError('Unsure of your video resolution request.'
'Try setting video_kpbs in the .ini file.')
'Try setting video_kpbs in PyLivestream configuration file (see README.md)')

if self.fps is None or self.fps < 20:
self.video_kbps: int = list(BRS.values())[bisect.bisect_left(list(BRS.keys()), x)]
Expand Down
Loading

0 comments on commit 6911cee

Please sign in to comment.