Skip to content

Instantly share code, notes, and snippets.

@junian
Last active September 13, 2025 13:59
Show Gist options
  • Select an option

  • Save junian/b41dd8e544bf0e3980c971b0d015f5f6 to your computer and use it in GitHub Desktop.

Select an option

Save junian/b41dd8e544bf0e3980c971b0d015f5f6 to your computer and use it in GitHub Desktop.

Revisions

  1. junian revised this gist Jan 23, 2017. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions twitch-recorder.py
    Original file line number Diff line number Diff line change
    @@ -15,7 +15,7 @@ class TwitchRecorder:
    def __init__(self):
    # global configuration
    self.client_id = "jzkbprff40iqj646a697cyrvl0zt2m6" # don't change this
    # get oauth token value by typing `livestreamer --twitch-oauth-authenticate` in terminal
    # get oauth token value by typing `streamlink --twitch-oauth-authenticate` in terminal
    self.oauth_token = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    self.ffmpeg_path = 'ffmpeg'
    self.refresh = 30.0
    @@ -107,8 +107,8 @@ def loopcheck(self):

    recorded_filename = os.path.join(self.recorded_path, filename)

    # start livestreamer process
    subprocess.call(["livestreamer", "--twitch-oauth-token", self.oauth_token, "twitch.tv/" + self.username, self.quality, "-o", recorded_filename])
    # start streamlink process
    subprocess.call(["streamlink", "--twitch-oauth-token", self.oauth_token, "twitch.tv/" + self.username, self.quality, "-o", recorded_filename])

    print("Recording stream is done. Fixing video file.")
    if(os.path.exists(recorded_filename) is True):
  2. junian revised this gist Jan 21, 2017. 1 changed file with 11 additions and 13 deletions.
    24 changes: 11 additions & 13 deletions twitch-recorder.py
    Original file line number Diff line number Diff line change
    @@ -2,10 +2,7 @@
    # You can read more details at: https://www.junian.net/2017/01/how-to-record-twitch-streams.html
    # original code is from https://slicktechies.com/how-to-watchrecord-twitch-streams-using-livestreamer/

    from urllib.request import urlopen, Request
    from urllib.error import URLError
    from threading import Timer

    import requests
    import os
    import time
    import json
    @@ -72,20 +69,21 @@ def check_user(self):
    # 2: not found,
    # 3: error
    url = 'https://api.twitch.tv/kraken/streams/' + self.username
    request = Request(url, headers={"Client-ID" : self.client_id})
    info = None

    status = 3
    try:
    info = json.loads(urlopen(request, timeout = 15).read().decode('utf-8'))
    r = requests.get(url, headers = {"Client-ID" : self.client_id}, timeout = 15)
    r.raise_for_status()
    info = r.json()
    if info['stream'] == None:
    status = 1
    else:
    status = 0
    except URLError as e:
    if e.reason == 'Not Found' or e.reason == 'Unprocessable Entity':
    status = 2
    else:
    status = 3
    except requests.exceptions.RequestException as e:
    if e.response:
    if e.response.reason == 'Not Found' or e.response.reason == 'Unprocessable Entity':
    status = 2

    return status, info

    def loopcheck(self):
    @@ -115,7 +113,7 @@ def loopcheck(self):
    print("Recording stream is done. Fixing video file.")
    if(os.path.exists(recorded_filename) is True):
    try:
    subprocess.call([ffmpegPath, '-err_detect', 'ignore_err', '-i', recorded_filename, '-c', 'copy', os.path.join(self.processed_path, filename)])
    subprocess.call([self.ffmpeg_path, '-err_detect', 'ignore_err', '-i', recorded_filename, '-c', 'copy', os.path.join(self.processed_path, filename)])
    os.remove(recorded_filename)
    except Exception as e:
    print(e)
  3. junian revised this gist Jan 18, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion twitch-recorder.py
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    # This code is based on tutorial by slicktechies modified as needed to use oauth token from Twitch.
    # You can read more details at:
    # You can read more details at: https://www.junian.net/2017/01/how-to-record-twitch-streams.html
    # original code is from https://slicktechies.com/how-to-watchrecord-twitch-streams-using-livestreamer/

    from urllib.request import urlopen, Request
  4. junian revised this gist Jan 18, 2017. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion twitch-recorder.py
    Original file line number Diff line number Diff line change
    @@ -93,6 +93,7 @@ def loopcheck(self):
    status, info = self.check_user()
    if status == 2:
    print("Username not found. Invalid username or typo.")
    time.sleep(self.refresh)
    elif status == 3:
    print(datetime.datetime.now().strftime("%Hh%Mm%Ss")," ","unexpected error. will try again in 5 minutes.")
    time.sleep(300)
    @@ -122,7 +123,7 @@ def loopcheck(self):
    print("Skip fixing. File not found.")

    print("Fixing is done. Going back to checking..")
    time.sleep(15)
    time.sleep(self.refresh)

    def main(argv):
    twitch_recorder = TwitchRecorder()
  5. junian revised this gist Jan 18, 2017. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions twitch-recorder.py
    Original file line number Diff line number Diff line change
    @@ -18,6 +18,7 @@ class TwitchRecorder:
    def __init__(self):
    # global configuration
    self.client_id = "jzkbprff40iqj646a697cyrvl0zt2m6" # don't change this
    # get oauth token value by typing `livestreamer --twitch-oauth-authenticate` in terminal
    self.oauth_token = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    self.ffmpeg_path = 'ffmpeg'
    self.refresh = 30.0
  6. junian revised this gist Jan 18, 2017. 1 changed file with 0 additions and 2 deletions.
    2 changes: 0 additions & 2 deletions twitch-recorder.py
    Original file line number Diff line number Diff line change
    @@ -22,8 +22,6 @@ def __init__(self):
    self.ffmpeg_path = 'ffmpeg'
    self.refresh = 30.0
    self.root_path = "/Users/junian/Documents/twitch"
    self.recorded_path = ""
    self.processed_path = ""

    # user configuration
    self.username = "juniantr"
  7. junian revised this gist Jan 17, 2017. 1 changed file with 5 additions and 0 deletions.
    5 changes: 5 additions & 0 deletions twitch-recorder.py
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,7 @@
    # This code is based on tutorial by slicktechies modified as needed to use oauth token from Twitch.
    # You can read more details at:
    # original code is from https://slicktechies.com/how-to-watchrecord-twitch-streams-using-livestreamer/

    from urllib.request import urlopen, Request
    from urllib.error import URLError
    from threading import Timer
    @@ -8,6 +12,7 @@
    import sys
    import subprocess
    import datetime
    import getopt

    class TwitchRecorder:
    def __init__(self):
  8. junian created this gist Jan 17, 2017.
    144 changes: 144 additions & 0 deletions twitch-recorder.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,144 @@
    from urllib.request import urlopen, Request
    from urllib.error import URLError
    from threading import Timer

    import os
    import time
    import json
    import sys
    import subprocess
    import datetime

    class TwitchRecorder:
    def __init__(self):
    # global configuration
    self.client_id = "jzkbprff40iqj646a697cyrvl0zt2m6" # don't change this
    self.oauth_token = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    self.ffmpeg_path = 'ffmpeg'
    self.refresh = 30.0
    self.root_path = "/Users/junian/Documents/twitch"
    self.recorded_path = ""
    self.processed_path = ""

    # user configuration
    self.username = "juniantr"
    self.quality = "best"

    def run(self):
    # path to recorded stream
    self.recorded_path = os.path.join(self.root_path, "recorded", self.username)

    # path to finished video, errors removed
    self.processed_path = os.path.join(self.root_path, "processed", self.username)

    # create directory for recordedPath and processedPath if not exist
    if(os.path.isdir(self.recorded_path) is False):
    os.makedirs(self.recorded_path)
    if(os.path.isdir(self.processed_path) is False):
    os.makedirs(self.processed_path)

    # make sure the interval to check user availability is not less than 15 seconds
    if(self.refresh < 15):
    print("Check interval should not be lower than 15 seconds.")
    self.refresh = 15
    print("System set check interval to 15 seconds.")

    # fix videos from previous recording session
    try:
    video_list = [f for f in os.listdir(self.recorded_path) if os.path.isfile(os.path.join(self.recorded_path, f))]
    if(len(video_list) > 0):
    print('Fixing previously recorded files.')
    for f in video_list:
    recorded_filename = os.path.join(self.recorded_path, f)
    print('Fixing ' + recorded_filename + '.')
    try:
    subprocess.call([self.ffmpeg_path, '-err_detect', 'ignore_err', '-i', recorded_filename, '-c', 'copy', os.path.join(self.processed_path,f)])
    os.remove(recorded_filename)
    except Exception as e:
    print(e)
    except Exception as e:
    print(e)

    print("Checking for", self.username, "every", self.refresh, "seconds. Record with", self.quality, "quality.")
    self.loopcheck()

    def check_user(self):
    # 0: online,
    # 1: offline,
    # 2: not found,
    # 3: error
    url = 'https://api.twitch.tv/kraken/streams/' + self.username
    request = Request(url, headers={"Client-ID" : self.client_id})
    info = None

    try:
    info = json.loads(urlopen(request, timeout = 15).read().decode('utf-8'))
    if info['stream'] == None:
    status = 1
    else:
    status = 0
    except URLError as e:
    if e.reason == 'Not Found' or e.reason == 'Unprocessable Entity':
    status = 2
    else:
    status = 3
    return status, info

    def loopcheck(self):
    while True:
    status, info = self.check_user()
    if status == 2:
    print("Username not found. Invalid username or typo.")
    elif status == 3:
    print(datetime.datetime.now().strftime("%Hh%Mm%Ss")," ","unexpected error. will try again in 5 minutes.")
    time.sleep(300)
    elif status == 1:
    print(self.username, "currently offline, checking again in", self.refresh, "seconds.")
    time.sleep(self.refresh)
    elif status == 0:
    print(self.username, "online. Stream recording in session.")
    filename = self.username + " - " + datetime.datetime.now().strftime("%Y-%m-%d %Hh%Mm%Ss") + " - " + (info['stream']).get("channel").get("status") + ".mp4"

    # clean filename from unecessary characters
    filename = "".join(x for x in filename if x.isalnum() or x in [" ", "-", "_", "."])

    recorded_filename = os.path.join(self.recorded_path, filename)

    # start livestreamer process
    subprocess.call(["livestreamer", "--twitch-oauth-token", self.oauth_token, "twitch.tv/" + self.username, self.quality, "-o", recorded_filename])

    print("Recording stream is done. Fixing video file.")
    if(os.path.exists(recorded_filename) is True):
    try:
    subprocess.call([ffmpegPath, '-err_detect', 'ignore_err', '-i', recorded_filename, '-c', 'copy', os.path.join(self.processed_path, filename)])
    os.remove(recorded_filename)
    except Exception as e:
    print(e)
    else:
    print("Skip fixing. File not found.")

    print("Fixing is done. Going back to checking..")
    time.sleep(15)

    def main(argv):
    twitch_recorder = TwitchRecorder()
    usage_message = 'twitch-recorder.py -u <username> -q <quality>'

    try:
    opts, args = getopt.getopt(argv,"hu:q:",["username=","quality="])
    except getopt.GetoptError:
    print (usage_message)
    sys.exit(2)
    for opt, arg in opts:
    if opt == '-h':
    print(usage_message)
    sys.exit()
    elif opt in ("-u", "--username"):
    twitch_recorder.username = arg
    elif opt in ("-q", "--quality"):
    twitch_recorder.quality = arg

    twitch_recorder.run()

    if __name__ == "__main__":
    main(sys.argv[1:])