Compare commits

..

No commits in common. "1ce273bc6102e2db53cce0c16f6c577c7697b715" and "4218dc827f91a9929554e7b85148ae9748bff733" have entirely different histories.

4 changed files with 36 additions and 73 deletions

4
.gitignore vendored
View File

@ -159,8 +159,8 @@ cython_debug/
# option (not recommended) you can uncomment the following to ignore the entire idea folder. # option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/ #.idea/
# Configuration # Channel List
config.ini channel_list.txt
# dl-stream log # dl-stream log
log.txt log.txt

View File

@ -2,49 +2,12 @@
<!--toc:start--> <!--toc:start-->
- [Auto TTV Grabber](#auto-ttv-grabber) - [Auto TTV Grabber](#auto-ttv-grabber)
- [tl;dr](#tldr) - [Getting started](#getting-started)
- [Configuration](#configuration)
- [Settings](#settings)
- [Adding streams](#adding-streams)
<!--toc:end--> <!--toc:end-->
## tl;dr ## Getting started
This script relies on [streamlink](https://streamlink.github.io) being installed. This script relies on [streamlink](https://streamlink.github.io) being in your systems path.
You can find all of its requirements on the their project page. You can find all of its requirements on the their project page.
This script loops through all of the channels in `config.ini`, This script loops through all of the channels in `channel_list.txt` once per minute checking to see if the channel is live via a HTTP request.
once per minute checking to see if the channel is live via a HTTP request. Once a channel is live a [streamlink](https://streamlink.github.io) subprocess spawns in the background downloading the stream to it's default location (`$HOME/Downloads/Stream/<channel_name>`)
Once a channel is live a `streamlink` subprocess spawns in the background,
downloading the stream to it's default location (`$HOME/Downloads/Stream/<channel_name>`)
If you wish to change this location, you can find out how to below
## Configuration
You can copy `config.ini.example` from the repo to `config.ini` to begin.
Leaving a setting blank will render it being ignored and using the default value.
### Settings
- `streamlink_location`: If `streamlink` is not in your path you can specify
its absolute location here. Otherwise it looks for it in your path.
- `download_location`: You can specify the absolute path to where you would like
to save the steams. A subdirectory will be created for each channel as it goes live.
If left blank the default location is: `$HOME/Downloads/Streams/<channel_name>`
- `skip_ads` : Takes a `True` or `False` value. If `True` you will need to have
installed the [ttvlol](https://github.com/2bc4/streamlink-ttvlol) `streamlink`
plugin. Setting this to `True` without doing so will cause the
`streamlink` subprocess to fail. Default value is `False`
### Adding streams
You will need to add the channels Twitch username to `config.ini`:
```ini
[streams]
1 = coney
2 = dougdoug
3 = parkzer
```
As for as I know there is no limit to how many streams you can add this way,
just make sure when you add a new stream it is incremented by one

View File

@ -1,7 +0,0 @@
[settings]
streamlink_location = ""
download_location = ""
skip_ads =
[streams]

51
main.py
View File

@ -7,11 +7,9 @@ import sys
import time import time
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
from typing import List
import requests import requests
channel_list: List[str] = [] channel_list = []
downloading = {} downloading = {}
# Default Config Settings # Default Config Settings
@ -20,7 +18,7 @@ download_location: str = f"{Path.home()}/Downloads/Streams"
skip_ads: bool = False skip_ads: bool = False
def load_config() -> None: def load_config():
print("Reading config file...") print("Reading config file...")
config = configparser.ConfigParser() config = configparser.ConfigParser()
config.read("config.ini") config.read("config.ini")
@ -40,17 +38,10 @@ def load_config() -> None:
skip_ads = bool(config["settings"]["skip_ads"]) skip_ads = bool(config["settings"]["skip_ads"])
print(f"Skip ads: {skip_ads}") print(f"Skip ads: {skip_ads}")
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") print("Config file loaded")
# TODO: Log better and properly def write_log(channel):
def write_log(channel: str) -> None:
"""Writes the latest stdout of a process to log.txt""" """Writes the latest stdout of a process to log.txt"""
with open("log.txt", "a") as log: with open("log.txt", "a") as log:
line = downloading[channel].stdout.readline() line = downloading[channel].stdout.readline()
@ -58,16 +49,15 @@ def write_log(channel: str) -> None:
log.write(line.decode()) log.write(line.decode())
def download_stream(channel: str) -> None: def download_stream(channel):
"""Downloads a given channel name in its own subprocess""" """Downloads a given channel name in its own subprocess"""
# TODO: Just clean this up at somepoint # TODO: Just clean this up at somepoint
addtional_parms: str = "" addtional_parms = ""
print(str(skip_ads))
if skip_ads: 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" 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" file_name = f"{channel}_{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.ts"
cmd: str = ( cmd = f"{streamlink_location} --loglevel none --retry-max 10 {addtional_parms} -o {download_location}/{channel}/{file_name} twitch.tv/{channel} best"
f"{streamlink_location} --loglevel none --retry-max 10 {addtional_parms} -o {download_location}/{channel}/{file_name} twitch.tv/{channel} best"
)
downloading[channel] = subprocess.Popen( downloading[channel] = subprocess.Popen(
[cmd], [cmd],
shell=True, shell=True,
@ -75,9 +65,10 @@ def download_stream(channel: str) -> None:
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
) )
write_log(channel)
def check_system() -> None: def check_system():
"""Makes sure everything is place for the script to run""" """Makes sure everything is place for the script to run"""
# Checks for config file # Checks for config file
@ -88,6 +79,14 @@ def check_system() -> None:
if not shutil.which("streamlink"): if not shutil.which("streamlink"):
sys.exit("ERROR: streamlink is not found in the systems path!") 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 # Make sure the download location exists
if not os.path.exists(download_location): if not os.path.exists(download_location):
print( print(
@ -96,7 +95,7 @@ def check_system() -> None:
os.makedirs(download_location) os.makedirs(download_location)
def stop_downloads() -> None: def stop_downloads():
"""Goes through every process and stops it if running""" """Goes through every process and stops it if running"""
print("\nCleaning up...") print("\nCleaning up...")
for name, proc in downloading.items(): for name, proc in downloading.items():
@ -104,12 +103,20 @@ def stop_downloads() -> None:
print(f"Stopping download of {name}") print(f"Stopping download of {name}")
def main() -> None: def main():
"""Main entry point of the app""" """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 # Run untill progam is killed
while True: while True:
# Exits the program if there is no channels to grab # 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------------------------------------") print("\n------------------------------------")
for channel in channel_list: for channel in channel_list:
channel = channel.strip() channel = channel.strip()
@ -123,7 +130,7 @@ def main() -> None:
download_stream(channel) download_stream(channel)
else: else:
print(f"{channel} is already downloading") print(f"{channel} is already downloading")
# write_log(channel) write_log(channel)
else: else:
print("\033[1m" + channel + "\033[0m is \033[31mnot live\033[0m.") print("\033[1m" + channel + "\033[0m is \033[31mnot live\033[0m.")
if channel in downloading: if channel in downloading: