#!/usr/bin/env python3 import configparser import os import shutil import subprocess import sys import time from datetime import datetime from pathlib import Path from typing import List import requests channel_list: List[str] = [] downloading = {} # Default Config Settings streamlink_location: str = "streamlink" download_location: str = f"{Path.home()}/Downloads/Streams" skip_ads: bool = False log: bool = False def load_config() -> None: print("Reading config file...") config = configparser.ConfigParser() config.read("config.ini") if ( config.has_option("settings", "streamlink_location") and not config["settings"]["streamlink_location"].strip() ): streamlink_location = config["settings"]["streamlink_location"] print(f"Streamlink location: {streamlink_location}") if ( config.has_option("settings", "download_location") and not config["settings"]["download_location"].strip() ): download_location = config["settings"]["download_location"] print(f"Download location: {download_location}") if config.has_option("settings", "skip_ads") and not config["settings"]["skip_ads"]: skip_ads = bool(config["settings"]["skip_ads"]) print(f"Skip ads: {skip_ads}") if config.has_option("settings", "log") and not config["settings"]["log"]: log = bool(config["settings"]["log"]) print(f"Logs: {log}") if len(config["streams"]) < 1: sys.exit("ERROR: No streams found in config.ini! See README.md for more info.") else: for index in range(1, len(config["streams"]) + 1): channel_list.append(config["streams"][str(index)]) print("Config file loaded") # TODO: Have steamlink itself check if the channel is live def is_live(channel: str) -> bool: """Checks if a channel is live on Twitch""" try: contents = requests.get("https://www.twitch.tv/" + channel).content.decode( "utf-8" ) if "isLiveBroadcast" in contents: return True else: return False except Exception: print(f"There was an issue checking if {channel} was life. Will try next time!") if channel in downloading: return True else: return False def download_stream(channel: str) -> None: """Downloads a given channel name in its own subprocess""" # TODO: Just clean this up at somepoint addtional_parms: str = "" if log: addtional_parms += f" --logfile {download_location}/{channel}/log.txt" if skip_ads: addtional_parms += " --twitch-proxy-playlist=https://lb-eu.cdn-perfprod.com,https://lb-eu2.cdn-perfprod.com,https://lb-na.cdn-perfprod.com,https://lb-as.cdn-perfprod.com,https://as.luminous.dev --twitch-disable-ads" file_name: str = f"{channel}_{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.ts" cmd: str = ( f"{streamlink_location} --retry-max 10{addtional_parms} -o {download_location}/{channel}/{file_name} twitch.tv/{channel} best" ) downloading[channel] = subprocess.Popen( [cmd], shell=True, start_new_session=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, ) def check_system() -> None: """Makes sure everything is place for the script to run""" # Checks for config file if not os.path.exists("config.ini"): sys.exit("ERROR: config.ini is not found! See README.md for more info.") # Checks if streamlink is in the systems path if not shutil.which("streamlink"): sys.exit("ERROR: streamlink is not found in the systems path!") # Make sure the download location exists if not os.path.exists(download_location): print( f"Download destination does not exist.\n Creating now at {download_location}" ) os.makedirs(download_location) def stop_downloads() -> None: """Goes through every process and stops it if running""" print("\nCleaning up...") for name, proc in downloading.items(): proc.terminate() print(f"Stopping download of {name}") def main() -> None: """Main entry point of the app""" # Run untill progam is killed while True: # Exits the program if there is no channels to grab print("\n------------------------------------") for channel in channel_list: channel = channel.strip() if is_live(channel): print(f"\n\033[1m{channel}\033[0m is \033[32mlive\033[0m!", end=" ") if channel not in downloading: download_stream(channel) else: print("\033[33m(Already Downloading)\033[0m") else: print( "\n\033[1m" + channel + "\033[0m is \033[31mnot live\033[0m.", end=" ", ) if channel in downloading: del downloading[channel] print("\033[35m(Stopping)\033[0m") time.sleep(1) # Wait one second before going to next channel print( "\n\n\033[3mLast checked: " + datetime.now().strftime("%H:%M:%S") + "\033[0m" ) print("------------------------------------") time.sleep(60) # Wait 60 Seconds before trying again if __name__ == "__main__": """This is executed when run from the command line""" try: check_system() load_config() main() finally: stop_downloads()