Well, this is like Day 1 pre-Alpha, so I wouldn't recommend jumping the gun just yet!
No worries on that. Mostly I just wanted to check that you're planning to support command line arguments in addition to pop-up boxes.
Well, the end goal is to have a web interface with minimal human intervention, so the final product wouldn't even use popups and things to enter like this. My intention for the interim, though, is to release various stand-alone components as I work through them. File creation is one of the more annoying aspects of Stream Links, so I thought it would make a good singular piece to release.
That all said, I have modified the code above so that it will use popup boxes if it can, otherwise it will use command lines, so it's all good now:
If I’m understanding the end goal of this project correctly - I am very excited for it to get off the ground.
Will it work like:
- Open program
- Select streaming service
- Search (eg “Marvel”)
- Tick movies you want to add
- Automatically gets added to channels server
?
What services are you hoping to have on this?
I thought I'd give this a whirl in OliveTin, so you can see what it would be like. Since you're not supporting passing arguments in a single command in your Python script, and Bash is super-easy for this kind of thing, I also made a couple of changes to see if they meet your goals.
I set it up to match the series name, and first airing year, with a query to the TVMaze API. This will pull the JSON data for the all season and episode data for that series, and create files in the same location you demo'd.
Here's what it looks like:
Here's the script:
#!/bin/bash
set -x
# Global variables
dvr="$1"
channelsHost=$(echo $dvr | awk -F: '{print $1}')
channelsPort=$(echo $dvr | awk -F: '{print $2}')
dvrShare=/mnt/$channelsHost-$channelsPort/Imports/TV/Hulu
showName=$(echo "$2" | sed -e 's/\b\(.\)/\u\1/g' -e 's/ /%20/g')
showYear="\"$3\""
episodePrefix="$4"
[[ "$episodePrefix" == "none" ]] && episodePrefix=""
# TVMaze API calls and post jq variable format changes
showID=$(curl -s "http://api.tvmaze.com/search/shows?q=$showName" | jq '.[] | select(.show.premiered | startswith('$showYear')).show.id')
episodeList=$(curl -s "http://api.tvmaze.com/shows/$showID/episodes" | jq -r '.[] | "S\((if .season < 10 then "0" else "" end) + (.season | tostring))E\((if .number < 10 then "0" else "" end) + (.number | tostring))"')
showName=$(echo "$showName" | sed 's/%20/ /g')
showYear=$(echo "$showYear" | sed 's/"//g')
[[ -n $episodePrefix ]] && episodePrefix="($episodePrefix) "
# Episode list to array and streamlink file creation
mapfile -t episodeArray <<< "$episodeList"
for episode in "${episodeArray[@]}"; do
seasonNumber="Season ${episode:1:2}"
mkdir -p "$dvrShare/$showName ($showYear)/$seasonNumber"
touch "$dvrShare/$showName ($showYear)/$seasonNumber/$episodePrefix$episode.strmlnk" \
&& echo Confirmed or added: "$dvrShare/$showName ($showYear)/$seasonNumber/$episodePrefix$episode.strmlnk"
done
This will run on any system with OliveTin properly installed, from anywhere on one's LAN or Tailnet. Output can be directed to the DVR_SHARE for however many Channels DVRs are configured in OliveTin. Mostly a proof of concept for you at this point, let me know what you think.
More like this:
-
Open Program
-
Provider Controls
-- Select Streaming Services
-- Set priority order for Streaming Services
-- Should have the ability to have all of them (or whatever the backend management service like JustWatch has available). -
Pick Programs and Availability
-- Search (e.g., "Hot Tub Time Machine")
-- Tick program you want to add (or, in the case of TV shows, pick the season [All, specific, only new])
-- Also have the ability to create a manual entry in case the underlying database does not have a record. -
Stream Link Generator
-- IF the Program is available on a selected Streaming Service, create the .strmlnk file ("Hot Tub Time Machine (2010).strmlnk")
-- Using the priority order for Streaming Services, get the direct link to the program ( Hoopla > Netflix, therefore, use Hoopla Stream Link)
-- IF it is a manual program, just create the .strmlnk -
Monitor
-- IF the Program becomes available on a higher priority Streaming Service, replace the contents of the .strmlnk file with that Stream Link.
-- IF the Program changes to not available on any selected Streaming Services, delete the .strmlnk file and run the Channels Prune operation (user option whether to allow this last step to happen automatically or not).
-- IF the Program is currently not available on any selected Streaming Services, but then becomes available, run the Stream Link Generator process from above on it.
-- IF the Program is deleted in Channels, remove the Tick for the Program in the interface so that the .strmlnk file is not automatically created.
No, that would break functionality. There are many use cases where either the Streaming Service does not have all episodes or the user only wants some because they've seen the others. Or maybe they only want new episodes when they become available. Interactivity and user selection is a requirement. The method for interactivity is still in the design phase. See the post above this one for more details.
Thanks, but what I've put out so far is about 2% of what the real Stream Link Manager is intended to do. I'm sure it will make a fine OliveTin deployment in the future, but for now, I'm just taking an open agile development approach with proof of concept components. For the foreseeable future, it would not be worth your time to try to take anything I've done or am doing and migrate it over, even for proof of concept. Too much is in flux and not anywhere near a final product.
ProtoType_Stream_Link_Maker.py
import os
import re
import urllib.parse
import datetime
def current_time():
return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f") + ": "
# Install simple-justwatch-python-api (https://github.com/Electronic-Mango/simple-justwatch-python-api) if not already installed
try:
from simplejustwatchapi.justwatch import search, details
except ImportError:
if os.name == "posix": # Unix-like systems (including macOS)
os.system("pip3 install simple-justwatch-python-api")
else:
os.system("pip install simple-justwatch-python-api")
# Check if the OS allows popup boxes
try:
import tkinter as tk
from tkinter import filedialog
from tkinter import simpledialog
from tkinter import messagebox
from tkinter import Tk
except ImportError:
# If popup boxes are not allowed, do not import tkinter
pass
# Get a directory path from the user using a file dialog or command line.
def get_directory():
while True:
if os.name == "posix": # Unix-like systems (including macOS)
directory_path = input("Enter a directory path: ")
else:
try:
import tkinter as tk
from tkinter import filedialog
root = tk.Tk()
root.withdraw() # Hide the main window
directory_path = filedialog.askdirectory(title="Select a base directory")
except ImportError:
directory_path = input("Enter a directory path: ")
# Validate if a value was entered
if directory_path:
break
else:
print(f"{current_time()} Error: Please provide a valid directory path.")
print("")
print(f"{current_time()} Chosen Directory: {directory_path}")
print("")
return directory_path
# Get user input from a popup box or command line.
def get_user_input(prompt):
while True:
if os.name == "posix": # Unix-like systems (including macOS)
user_input = input(prompt)
else:
try:
import tkinter as tk
from tkinter import simpledialog, messagebox
root = tk.Tk()
root.withdraw() # Hide the main window
user_input = simpledialog.askstring("Input", prompt)
root.destroy() # Close the pop-up window
except ImportError:
user_input = input(prompt)
# Validate if a value was entered
if user_input is not None and user_input.strip():
return user_input.strip() # Remove leading/trailing spaces
else:
print(f"{current_time()} Error: Please provide a valid input.")
# Get user selection from buttons or command line list.
def get_button_selection(prompt, options):
while True:
if os.name == "posix": # Unix-like systems (including macOS)
print(prompt)
for i, option in enumerate(options, start=1):
print(f"{i}. {option}")
try:
choice = int(input("Enter the number corresponding to your choice: "))
if 1 <= choice <= len(options):
return options[choice - 1]
else:
print("Invalid choice. Please try again.")
except ValueError:
print("Invalid input. Please enter a valid number.")
else:
try:
import tkinter as tk
root = tk.Tk()
root.withdraw() # Hide the main window
# Create a dialog box with radio buttons for each option
dialog = tk.Toplevel(root)
dialog.title("Select an Option")
# Calculate the height of the dialog based on the number of options
dialog_height = 150 + len(options) * 30
dialog.geometry(f"300x{dialog_height}")
# Add the prompt to the dialog
tk.Label(dialog, text=prompt).pack()
# Create a variable to store the selected option
selected_option = None
def select_option(option):
nonlocal selected_option
selected_option = option
dialog.destroy()
for option in options:
tk.Button(dialog, text=option, command=lambda o=option: select_option(o)).pack()
# Wait for the user to select an option
dialog.wait_window()
# Return the selected option
if selected_option:
return selected_option
else:
print(f"{current_time()} Error: Please select an option.")
except ImportError:
return input(prompt)
# Remove invalid characters (e.g., colons, slashes, etc.)
def sanitize_name(name):
sanitized = re.sub(r'[\\/:*?"<>|]', '', name)
return sanitized
# Create a directory if it doesn't exist.
def create_directory(directory_path):
if not os.path.exists(directory_path):
os.makedirs(directory_path)
# Create season folders with proper naming.
def create_season_folders(base_directory, starting_season, ending_season):
for season_number in range(starting_season, ending_season + 1):
season_folder_name = f"Season {season_number:02}"
season_folder_path = os.path.join(base_directory, season_folder_name)
create_directory(season_folder_path)
# Create episode files within a season folder.
def create_episode_files(season_folder, episode_count, prefix, episode_url):
for episode_number in range(1, episode_count + 1):
episode_name = f"S{season_folder[-2:]}E{episode_number:02}"
if prefix != "None":
episode_name = f"{prefix} {episode_name}"
episode_name += ".strmlnk"
episode_file_path = os.path.join(season_folder, episode_name)
print(f"{current_time()} Creating: {episode_file_path}")
if not os.path.exists(episode_file_path):
with open(episode_file_path, 'w') as file:
file.write(episode_url)
# Create movie files
def create_movie_file(movie_directory, movie_name_base, movie_url):
movie_name = f"{movie_name_base}"
movie_name += ".strmlnk"
movie_file_path = os.path.join(movie_directory, movie_name)
print(f"{current_time()} Creating: {movie_file_path}")
if not os.path.exists(movie_file_path):
with open(movie_file_path, 'w') as file:
file.write(movie_url)
# Find all the Streaming Services for the selected Program
def extract_offer_info(offers):
result = []
for offer in offers:
name = offer.package.name
price_string = offer.price_string if offer.price_string is not None else "$0.00"
url = urllib.parse.unquote(offer.url) # Decode the URL
result.append({"name": name, "price_string": price_string, "url": url})
return result
# Input the country code to search
def get_country_code():
while True:
country_code_input = input("Enter a 2-letter country code (or press Enter for default 'US'): ")
if not country_code_input:
return "US" # Default value
elif len(country_code_input) == 2 and country_code_input.isalpha():
return country_code_input.upper()
else:
print("")
print("Invalid input. Please enter a valid 2-letter country code.")
print("")
# Input the language code to search
def get_language_code():
while True:
language_code_input = input("Enter a 2-letter language code (or press Enter for default 'en'): ")
if not language_code_input:
return "en" # Default value
elif len(language_code_input) == 2 and language_code_input.isalpha():
return language_code_input.lower()
else:
print("")
print("Invalid input. Please enter a valid 2-letter language code.")
print("")
# Input the number of search results to return
def get_num_results():
while True:
try:
num_results_input = input("Enter the number of search results (or press Enter for default '9'): ")
if not num_results_input:
return 9 # Default value
num_results = int(num_results_input)
if num_results > 0:
return num_results
else:
print("")
print("Please enter a positive integer.")
print("")
except ValueError:
print("")
print("Invalid input. Please enter a valid positive integer.")
print("")
# Main Function
def main():
print("")
# Input the country code
country_code = get_country_code()
print("")
print(f"{current_time()} Selected country code: {country_code}")
print("")
# Input the language code
language_code = get_language_code()
print("")
print(f"{current_time()} Selected language code: {language_code}")
print("")
# Input the number of search results
num_results = get_num_results()
print("")
print(f"{current_time()} Selected number of search results: {num_results}")
print("")
# Loop to run again
while True:
# Search for a program
while True:
print("")
program_search = input("Enter the program to search for: ")
program_search_base = search(program_search, country_code, language_code, num_results, True)
# Extract the titles, release years, and object types from the response
program_search_results = [(entry.title, entry.release_year, entry.object_type, entry.entry_id) for entry in program_search_base]
# Display a list of options to the user
print("")
print("Select an option (or enter 0 to cancel and retry):")
print("")
for i, (title, release_year, object_type, entry_id) in enumerate(program_search_results, start=1):
print(f"{i}. {title} ({release_year}) | {object_type}")
while True:
# Get user input for the selected option
print("")
program_search_selected = input("Enter the number of the option you want to choose: ")
print("")
# Validate user input
try:
program_search_index = int(program_search_selected) - 1
if program_search_index == -1:
print("")
print(f"{current_time()} Selection canceled. Returning to the top.")
print("")
retry_step = 0
break
elif 0 <= program_search_index < len(program_search_base):
program_search_selected_entry_id = program_search_base[program_search_index].entry_id
program_search_selected_object_type = program_search_base[program_search_index].object_type
program_search_selected_program = f"{program_search_base[program_search_index].title} ({program_search_base[program_search_index].release_year})"
print("")
print(f"{current_time()} You selected: {program_search_selected_program}")
print(f"{current_time()} The selected program is a: {program_search_selected_object_type}")
print(f"{current_time()} The corresponding entry ID is: {program_search_selected_entry_id}")
print("")
retry_step = 1
break
else:
print("")
print(f"{current_time()} Invalid option. Please choose a valid number.")
print("")
retry_step = 3
continue
except ValueError:
print("")
print(f"{current_time()} Invalid input. Please enter a valid number.")
print("")
retry_step = 3
continue
if retry_step == 0:
continue
elif retry_step == 1:
break
# Select a Streaming Services Option
while True:
# Get the Streaming Services Offer based upon the selected Program
services_search_base = details(program_search_selected_entry_id, country_code, language_code, True)
services_search_results = extract_offer_info(services_search_base.offers)
if not services_search_results:
print(f"{current_time()} {program_search_base[program_search_index].title} is not currently available on any Straming Service.")
print("")
run_create = False
break
else:
run_create = True
# Display a list of Streaming Services Options to the user
print("")
print("Select a Streaming Service (or enter 0 to cancel and exit):")
print("")
for i, info in enumerate(services_search_results, start=1):
print(f"{i}. {info['name']} ({info['price_string']})")
while True:
# Get user input for the selected Streaming Services Option
print("")
services_search_selected = input("Enter the number of the Streaming Service Option you want to choose: ")
print("")
# Validate user input
try:
services_search_index = int(services_search_selected) - 1
if services_search_index == -1:
print("")
print(f"{current_time()} Cancelling...")
print("")
run_create = False
retry_step = 1
break
elif 0 <= services_search_index < len(services_search_results):
services_search_selected_service = services_search_results[services_search_index]
services_search_selected_url = services_search_selected_service['url'] # Save the URL
print("")
print(f"{current_time()} You selected: {services_search_selected_service['name']} ({services_search_selected_service['price_string']})")
print(f"{current_time()} The corresponding URL is: {services_search_selected_url}")
print("")
retry_step = 1
break
else:
print("")
print(f"{current_time()} Invalid option. Please choose a valid number.")
print("")
retry_step = 3
continue
except ValueError:
print("")
print(f"{current_time()} Invalid input. Please enter a valid number.")
print("")
retry_step = 3
continue
if retry_step == 0:
continue
elif retry_step == 1:
break
# Create directories (if ncessary) and file(s)
if run_create:
program_name = sanitize_name(program_search_selected_program)
print("")
print("Select a top level directory for the Stream Link(s):")
print("")
base_directory = get_directory()
if program_search_selected_object_type == "SHOW":
prefix_options = ["None", "(DUB)", "(SUB)"]
selected_prefix = get_button_selection("Select a Prefix option for the episodes:", prefix_options)
starting_season = int(get_user_input("Enter the Starting Season Number: "))
ending_season = int(get_user_input("Enter the Ending Season Number: "))
create_directory(os.path.join(base_directory, program_name))
create_season_folders(os.path.join(base_directory, program_name), starting_season, ending_season)
for season_number in range(starting_season, ending_season + 1):
season_folder_name = f"Season {season_number:02}"
season_folder_path = os.path.join(base_directory, program_name, season_folder_name)
episode_count = int(get_user_input(f"Number of Episodes for {season_folder_name}: "))
create_episode_files(season_folder_path, episode_count, selected_prefix, services_search_selected_url)
print(f"{current_time()} Directory structure and files created successfully.")
elif program_search_selected_object_type == "MOVIE":
create_movie_file(base_directory, program_name, services_search_selected_url)
print(f"{current_time()} File created successfully")
else:
print("")
print(f"{current_time()} ERROR: Unknown Program Type")
print("")
# Check to re-run
rerun_options = ["Yes", "No"]
rerun_select = get_button_selection("Do you wish to run again? (Yes/No):", rerun_options)
if rerun_select == "Yes":
continue
elif rerun_select == "No":
break
else:
print("")
print(f"{current_time()} Error encountered, exiting...")
print("")
break
# Other Actions
print("")
print(f"{current_time()} End of current commands.")
print("")
exit()
if __name__ == "__main__":
main()
The above script is a prototype proof-of-concept for the program search, selection of a program, and creation of expected Stream Link files. This is a fully functioning application with the following caveats:
-
TV Shows are not getting individual episode URLs (yet). The API that is used to access the base data needs an update before that is possible. Also, given this current situation, I have opted not to get more granular with episode selection at this time.
-
The Stream Links that are created might not be valid. I have especially noticed an issue with Disney+ that I'm working on a higher level approach to address. Although I know how to solve the issue for Disney+, I do not want to create fixes for individual services as it will be way too much to manage.
-
In a similar vein, although almost all Hulu programs are available on Disney+, the underlying data provider is not up to date on this fact and will usually only offer Hulu instead of Disney+ and Hulu. Again, I already have a resolution for this, but would rather avoid dealing with a specific service this way.
That all said, here's how it looks:
At the beginning, you are prompted for certain conditions for searching. If you want the default options (United States
, English
, 9 Results
), then you can just hit enter and parse past them.
At this point you can search for whatever you are looking for, not case sensitive. If you get enter-happy or forget to put anything, it will just return the most popular programs right now. Otherwise, you will see this:
From here, you can select 0
to search again, or select one of the numbers. If you mistype, it will warn you and let you select again until you make a valid choice. In this case, I'm going to select the first option:
The application confirms your choice and then offers you everywhere it is available and at what price (assuming you have a subscription for the $0.00
options). If it is not available anywhere, you will be told so and forced to the end of the routine (more on this later). Otherwise, similar to the prior step, you must select a valid option to move on, or 0
to cancel and maybe try again. I'm going to choose 9
for YouTube:
A confirmation appears for the selection and the URL. At this point, you would select a the directory where you want the file to be created (or, if a show, the parent directory). Note that because I am using Windows I am getting a pop-up selection box, but command line only systems would have to type in a directory path. This is the case for all popups below. Later, these will not exist and instead will default into their own directory in the Imports directory under Channels.
After selecting the folder, everything progresses as normal:
As can be seen above, a file is created automatically with the correct name and Stream Link inside!
Afterwards, you'll be asked if you want to run again and do another search.
This will bring you back to the search menu. If you do any of the earlier opportunities to cancel, you will also end up here. Now that we are here, let's go through a TV Show. Back to the prior test, I'm going to select 6
.
Now I get prompts for prefixes, number of seasons, and number of episodes per season. In the future, this will not be a selection at this point but part of the management system:
This all results in individual files being created:
Take note that the program had an invalid character (a colon :
) that was removed. This would happen with a movie, as well.
And that's it! With this, we have proven that we can search a 3rd party source and create Stream Links based upon it. I'll also be looking into the reverse of this with selecting a Streaming Service and being presented with the newest additions to see if you want to create the Stream Links.
Enjoy!
This is looking good but is there a way to prevent the script from exiting after selection of one show/movie? I got multiple shows/movies that I want to add so going back and opening up the script all the time could be tedious
A quick status update...
Stream Link Manager for Channels is 90% ready for an alpha release. Here's where everything stands:
-
Settings
-- Set Channels URL
---- 100% Complete
---- Defaults to current server
---- Can be set to any URL/Port
-- Set Channels Folder
---- 100% Complete
---- Defaults to current folder
---- Can navigate and select through the text menu to any folder
-- Select Streaming Services
---- 100% Complete
---- Automatically updates list of available streaming services (adds and removes)
---- Limited to streaming services available in set country
-- Prioritize Streaming Services
---- 100% Complete
---- Newly selected streaming service ends up lowest priority
---- Can move streaming services up and down in prioritization
-- Set Search Defaults
---- 100% complete
---- Defaults to Location: United States, Language: English, Number of Results: 9
---- Can override during search
---- Automatic set of location during first launch to get appropriate Streaming Services
-- Advanced / Experimental Settings
---- Currently only have converting Hulu stream links to Disney+
---- Option exists, but have not implemented (work in progress)
---- OPEN QUESTION: Are there more to consider? -
Search and Bookmark Programs
-- 100% Complete
-- Can search for any movie/show (or get most popular), get descriptions
--
-- Can select any movie/show
-- Movies/Shows always remain bookmarked (warns if already bookmarked)
-- When selecting a show, pick which episodes are unwatched
--
-- OPEN QUESTION: Should functionality for episode prefixes [i.e., "(SUB) ", "(DUB) "] be added? -
Add Manual Programs
-- 100% Complete
-- Has all the same functionality of search, just have to enter program name and release year
-- Manually enter in Stream Link URLs (never removed)
-- Keeps track of manual programs versus search programs -
Check for New Episodes
-- 100% Complete
-- Detects any added episode for search bookmarks and adds to bookmarks
-- New episodes are always "unwatched" -
Import Program Updates from Channels
-- 100% Complete
-- Detects deleted Stream Link files
-- Marks movie/episode as watched, thus eliminating Stream Link creation -
Manually Modify Programs and Episodes
-- 100% Complete
-- Can change name, release year, watched status, and stream link URL (this is overwritten for programs added using the search method)
-- Can add new episodes
-- No impact on Stream Link generation -
Generate Stream Links
-- 90% Complete
-- Watched/Unwatched status and availability on selected streaming services determines if a Stream Link is created
-- Gets stream link URLs
---- 100% working for movies (search and manual) and manual shows
---- Open issue getting stream link URLs for search shows (work in progress)
---- Open issue cleaning up all stream link URLs to get rid of tracking and have an appropriate format for deep linking (work in progress)
------ Also need to add Hulu to Disney+ functionality
-- 100% creates all necessary directories and files in the expected location and populates with stream link URLs
-- 100% removes watched/unavailable Stream Links and deletes empty directories. -
Prune/Scan in Channels
-- 100% Complete
-- Runs a "Prune Personal Media" in Channels
-- Runs a "Scan Personal Media" in Channels -
Future Functionality (not for this build)
-- Scheduling system to run Check for New Episodes, Import Program Updates from Channels, Generate Stream Links, and Prune/Scan in Channels in succession
-- Web page GUI
-- Docker container and/or local executable
Seeking Alpha Testers
Stream Link Manager for Channels Alpha is now complete and in closed unit testing. At the moment, that just includes me, but I'll soon be interested in expanding to a wider pool of people with different setups to fully kick the tires. If you're interested, please PM me with the following information:
- Operating System (i.e., Windows, MacOS, Linux)
- Streaming Services you use (i.e., Netflix, Disney+, Max, etc...)
- Familiarity/Comfort level with Python and Batch/Bash commands (not required to have; possessing little to no knowledge is useful, too)
So for me, this is:
- Windows
- Disney+, Hulu, Kanopy, Hoopla
- Moderate-Strong understanding of Python, High level for Batch, Low-Moderate for Bash
Status Updates
-
Startup
--
-- There is now a batch/bash script available for running
-- Users no longer need to know anything about Python
-- Batch/Bash script automatically does the installation, updates the code, and launches the program -
Scheduling
--
-- Can now set a time for a process to run daily that:
---- Checks for New Episodes
---- Imports Program Updates from Channels
---- Generates Stream Links
---- Runs a Prune/Scan in Channels
-- Default time is the first time you run the program
-- Can always create/update and remove the scheduled task -
Stream Link URLs
--
-- Can now get Stream Link URLs for individual episodes 100% (Level Up! New skill acquired... GraphQL)
---- Episode data is currently not as good as hoped from source. This is a function of the provider and there is nothing I can do about it save them updating their own data or me building my own database.
---- Some sources like Netflix and Disney+ will only bring you to the series landing page
---- Other sources like Paramount+ and Hulu have very good episode data
---- However, with something like Hulu, if there is a program available both in SUB and DUB, they are separate listings. The Stream Link URLs tend to be the SUB versions.
---- Workaround detailed below
-- Hulu to Disney+ Conversion working 100% (with same warning from above)
-- URL Clean-up does many different levels
---- Might be too aggressive for some services
---- Might be other fixes that can be done for individual services that I have not checked
---- This will be a core Alpha Tester activity -
Manual/Override Controls
--
-- As a workaround to the episode issues noted above, any program/episode can have its URL be overwritten with a manual entry.
-- Can now add any Prefix you want to episodes [i.e., (SUB), (DUB)] -
General Quality Improvements
-- Further simplification, removing repeating elements, lower resource utilization, bug fixes
-- Program checks for and removes rogue files from folders. For instance, if you added a prefix after an episode file was originally created, it will create the prefix file and get rid of the non-prefix one.
-- Notification when an override Stream Link is used instead of searching for one
Coming Soon
- Video Demonstration
- Closed Alpha Testing
- Migration to Github
- Alpha Open Release
I pinned the topic globally to get some visibility to the alpha.
Just noticed a typo:
That looks great. I will PM you my info.
Hehehe, thanks for proving why I need testers and other sets of eyes!
FYI, now fixed!
Not a problem, it happens to the best of us.
A big part of my job during the week is to do code and document reviews. When I really focus on a review, I can find small details. Not to say I notice everything but I'm trained to look for these kind of things. It's second nature to me now.
Anyway, this project looks great and I am looking forward to helping with testing it.
I am very interested in this. However, I am in no way a Dev or very knowledgeable with Python/Bash stuff, etc. I can pretty easily follows directions on what to do to set something like this up, but not sure how difficult that would be in my case.
Is it pretty easy to do with some directions? Just don't want to waste your time if I should just wait but interested in this becoming a thing.
In no way would a tester like you be a waste of time! My goal is that you shouldn't need to know anything about coding, just how to double click a file. Please PM me with the answers to the above so I can make sure I have your info when I'm ready to release to the group. Before then, I'll be posting a demo video, too. I definitely need non-techie eyes to make sure the interface and navigation makes sense.