Skip to content

Instantly share code, notes, and snippets.

@chidea
Last active June 22, 2019 21:08
Show Gist options
  • Select an option

  • Save chidea/1e7b48c56b5990cc5e319133d4e7a074 to your computer and use it in GitHub Desktop.

Select an option

Save chidea/1e7b48c56b5990cc5e319133d4e7a074 to your computer and use it in GitHub Desktop.

Revisions

  1. chidea revised this gist Jun 22, 2019. 1 changed file with 7 additions and 5 deletions.
    12 changes: 7 additions & 5 deletions yt.py
    Original file line number Diff line number Diff line change
    @@ -33,17 +33,19 @@ def _skip(self, stream):
    return self._gen(stream)
    from itertools import tee
    urls, _urls = tee(self._gen(stream))
    j=0
    j=-1
    for i, (t, u) in enumerate(_urls):
    if u == self._last_m3u8: j=i
    print(j, i)
    if j==i:
    if u == self._last_m3u8:
    j=i+1
    break
    if j<0:
    print('couldn\'t find m3u8:', self._last_m3u8)
    pass
    else:
    print('previous stream found at ', j, 'th')
    for _ in range(j):
    next(urls)
    print(j+1, 'urls skipped')
    print(j, 'urls skipped')
    return urls
    def _gen(self, stream):
    for i in range(10):
  2. chidea revised this gist Jun 20, 2019. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions yt.py
    Original file line number Diff line number Diff line change
    @@ -3,6 +3,7 @@
    # or "8XJ6r4U171I"
    # or even "https://www.youtube.com/freecodecamp/live"
    # Proxy player can be started with `ffplay -rtsp_flags listen rtsp://0.0.0.0:12000/live.sdp`
    # omit proxy player address to play it locally by starting localhost proxy player.

    from youtube_dl import YoutubeDL as YDL
    from time import time as now, sleep
  3. chidea created this gist Jun 20, 2019.
    190 changes: 190 additions & 0 deletions yt.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,190 @@
    # python3 yt.py <URL> [proxy player address]
    # URL can be something like "https://www.youtube.com/watch?v=8XJ6r4U171I"
    # or "8XJ6r4U171I"
    # or even "https://www.youtube.com/freecodecamp/live"
    # Proxy player can be started with `ffplay -rtsp_flags listen rtsp://0.0.0.0:12000/live.sdp`

    from youtube_dl import YoutubeDL as YDL
    from time import time as now, sleep
    from urllib.request import urlopen

    BUF_SEC = 30 # seconds to buffer

    class Youtube():
    def __init__(self, url):
    self._url = url
    self._last_m3u8 = ''
    def _select_stream(self):
    with YDL({'quiet':True}) as ydl: #, 'format':'bestaudio/best'}) as ydl:
    info = ydl.extract_info(self._url, False)
    #return info['formats'][0]['url'] # worst
    max_abr = None
    abr = 0
    # find best abr & worst vbr
    for f in reversed(info['formats']):
    if f['abr'] >= abr:
    abr = f['abr']
    max_abr = f
    else: break
    return max_abr['url']
    def _skip(self, stream):
    if not self._last_m3u8:
    return self._gen(stream)
    from itertools import tee
    urls, _urls = tee(self._gen(stream))
    j=0
    for i, (t, u) in enumerate(_urls):
    if u == self._last_m3u8: j=i
    print(j, i)
    if j==i:
    print('couldn\'t find m3u8:', self._last_m3u8)
    pass
    else:
    for _ in range(j):
    next(urls)
    print(j+1, 'urls skipped')
    return urls
    def _gen(self, stream):
    for i in range(10):
    try:
    with urlopen(stream) as f:
    tlen = 0
    for l in f:
    if l.startswith(b'#EXTINF:'):
    tlen = float(l[8:-2])
    elif l.startswith(b'https://'):
    turl = l[:-1].decode()
    yield tlen, turl
    break
    except KeyboardInterrupt:
    break
    except:
    sleep(.1*(i+1))
    print('error opening stream:', stream, 'retrying..')
    continue

    def gen(self):
    while True:
    for t, u in self._skip(self._select_stream()):
    yield t, u

    class Streamer():
    def __init__(self, addr, url):
    from sys import platform
    if platform == 'win32':
    _codec = '-c:a opus -strict -2'
    else:
    _codec = '-c:a libopus'
    self.is_local = addr[0] in ('localhost', '127.0.0.1')
    from subprocess import Popen, PIPE
    # ffmpeg -re option (realtime emulation) is avoided. Google server closes such slow reading connections.
    # aac re-encoding to avoid 'AAC with no global headers' error
    self._prc = Popen(('ffmpeg -hide_banner -i - -vn %s -b:a 52k -f rtsp -rtsp_transport tcp rtsp://%s:%d/live.sdp' % ('-c:a aac' if self.is_local else _codec, *addr)).split(), stdin=PIPE)
    self.stream_started, self.stream_loaded = 0, 0
    self.yt = Youtube(url)#'Vls4h1GAP-c'
    def __del__(self):
    self._prc.kill()
    def stream(self):
    try:
    #import os, sys
    #with os.fdopen(sys.stdout.fileno(), 'wb') as w:
    if not self.stream_started: self.stream_started = now()
    for t, u in self.yt.gen():
    #print(t, u)
    while True:
    try:
    b = urlopen(u).read()
    break
    except:
    print('error opening m3u8:', u, 'retrying...')
    sleep(.1)
    continue
    #run(('ffprobe -hide_banner -i -').split(), input=b)
    self._prc.stdin.write(b)
    self.stream_loaded += t # usually 5.005 seconds each.
    t = self.stream_started + self.stream_loaded
    target = now() + BUF_SEC
    if t > target:
    #print('sleep', t-target)
    sleep(t - target)
    self.yt.last_url = u # last url saved for skipping already streamed parts
    except KeyboardInterrupt:
    pass
    #from time import time
    #from sched import scheduler
    #def record(url, speed=1):
    # with open('test.ts', 'wb') as fw:
    # def play(url):
    # print(url)
    # with urlopen(url) as fr:
    # fw.write(fr.read())
    # #run(['ffmpeg', '-hide_banner', '-i', url, '-', str(name)+'.aac'])
    #
    # s = scheduler(time)
    # tlen = 0
    # for t, u in read_m3u8(url):
    # #play(u)
    # s.enter(tlen, 1, play, (l[:-1].decode(),))
    # tlen += t/speed
    # s.run()


    # is_serv = addr[0] == '0.0.0.0'
    # with open_socket() as w:
    # if is_serv:
    # w.bind(addr)
    # w.listen(1)
    # c, a = w.accept()
    #
    # from subprocess import Popen, PIPE
    # converter = Popen(('ffmpeg -hide_banner -i - -vn %s -f opus -b:a 52k -'%(_codec,)).split(), stdin=PIPE, stdout=PIPE)
    #
    # def out_stream(u):
    # converter.stdin.write(urlopen(u).read())
    # b = converter.stdout.read()
    # #b = strip_audio(urlopen(u).read())
    # for i in range(len(b)//1024):
    # if is_serv:
    # c.sendall(b[1024*i:1024*i+1024])
    # else:
    # w.sendto(b[1024*i:1024*i+1024], addr)
    # if len(b)%1024:
    # if is_serv:
    # c.sendall(b[1024*i:1024*i+1024])
    # else:
    # w.sendto(b[1024*(i+1):], addr)
    # #w.write(urlopen(u).read())
    # #w.flush()
    # s = scheduler(time)
    # tt = 0
    # cnt = 0
    # for t, u in read_m3u8(url):
    # s.enter(tt, 1, out_stream, (u,))
    # if cnt>1:
    # tt+=t
    # cnt+=1
    # s.run()

    #def open_socket():
    # from socket import socket, AF_INET, SOCK_DGRAM
    # sock = socket(AF_INET, SOCK_DGRAM)
    # sock.settimeout(1)
    # return sock
    #def strip_audio(b):
    # global _codec
    # from subprocess import run, PIPE
    # return run(('ffmpeg -hide_banner -i - -vn '+_codec+' -f opus -b:a 52k -').split(), input=b, stdout=PIPE).stdout

    if __name__ == '__main__':
    from sys import argv
    addr = (argv[2] if len(argv)>2 else 'localhost', 12000)
    streamer = Streamer(addr, argv[1])
    if streamer.is_local:
    from subprocess import Popen
    listener=Popen('ffplay -rtsp_flags listen rtsp://localhost:12000/live.sdp'.split())

    try:
    streamer.stream()
    except KeyboardInterrupt:
    if streamer.is_local:
    listener.kill()