#!/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 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}") print("Config file loaded") def write_log(channel: str) -> None: """Writes the latest stdout of a process to log.txt""" with open("log.txt", "a") as log: line = downloading[channel].stdout.readline() if line: log.write(line.decode()) 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 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} --loglevel none --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, ) write_log(channel) 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!") # TODO: Combine the channel_list into the config # Checks if the channel_list exists and if not makes one if not os.path.exists("channel_list.txt"): print("ERROR:'channel_list.txt' does not exist, creating now!") with open("channel_list.txt", "w"): pass # Creates empty file sys.exit("Please populate the channel_list.txt with one channel per line!") # 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""" # Grab all the channels from channel_list.txt and put them in a list with open("channel_list.txt", "r") as file: # channel_list = file.readlines() channel_list = [ line for line in file if line.strip() ] # Removes all white spaces per line # Run untill progam is killed while True: # Exits the program if there is no channels to grab if not channel_list: sys.exit("Please populate the channel_list.txt with one channel per line!") print("\n------------------------------------") for channel in channel_list: channel = channel.strip() # TODO: Have steamlink itself check if the channel is live contents = requests.get("https://www.twitch.tv/" + channel).content.decode( "utf-8" ) if "isLiveBroadcast" in contents: print(f"\033[1m{channel}\033[0m is \033[32mlive\033[0m!") if channel not in downloading: download_stream(channel) else: print(f"{channel} is already downloading") write_log(channel) else: print("\033[1m" + channel + "\033[0m is \033[31mnot live\033[0m.") if channel in downloading: del downloading[channel] print(f"{channel} is no longer downloading") time.sleep(1) # Wait one second before going to next channel print( "\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()