From cd2fb7a452ec4ce54f52a472c5919e23832a57ab Mon Sep 17 00:00:00 2001 From: cyberboy666 Date: Mon, 10 Jun 2024 13:41:59 +1000 Subject: [PATCH] initial commit --- .gitignore | 2 + obs_scheduler.py | 207 +++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 3 + static/style.css | 17 ++++ templates/index.html | 61 +++++++++++++ 5 files changed, 290 insertions(+) create mode 100644 .gitignore create mode 100644 obs_scheduler.py create mode 100644 requirements.txt create mode 100644 static/style.css create mode 100644 templates/index.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fac1b9b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +schedule.json +obs_stream.log diff --git a/obs_scheduler.py b/obs_scheduler.py new file mode 100644 index 0000000..907daf5 --- /dev/null +++ b/obs_scheduler.py @@ -0,0 +1,207 @@ +import os +import time +import threading +import logging +import json +from flask import Flask, request, jsonify, render_template, redirect, url_for +import m3u8 +from datetime import datetime, timedelta +import obsws_python as obs + + +# Configure logging +logging.basicConfig(filename='obs_stream.log', level=logging.INFO, format='%(asctime)s:%(levelname)s:%(message)s') + +# Flask setup +app = Flask(__name__) +schedule_file = 'schedule.json' + +# OBS WebSocket connection configuration +host = 'localhost' +port = 4444 +password = 'your_password' + +# Global variables to control the streaming +stream_active = False +current_playlist = "" +schedule = [] +stop_streaming = threading.Event() +stream_schedule = {} # To store multiple scheduled start/stop times + +def parse_playlist(playlist_path): + playlist = m3u8.load(playlist_path) + videos = [] + run_time = 0 + for segment in playlist.segments: + # print(vars(segment)) + videos.append({'title': '.'.join(segment.title.split('.')[:-1]), 'start_time': run_time}) + run_time = run_time + segment.duration + return videos + +def generate_schedule(start_time, playlist_path): + videos = parse_playlist(playlist_path) + schedule = [] + current_time = start_time + for video in videos: + end_time = current_time + timedelta(seconds=video['duration']) + schedule.append({'title': video['title'], 'start_time': current_time, 'end_time': end_time}) + current_time = end_time + return schedule + +def start_stream(playlist_path, stream_name): + global stream_active, current_playlist, schedule + try: + # Initialize the OBS WebSocket client using ReqClient (connects automatically) + cl = obs.ReqClient(host=host, port=port, password=password, timeout=3) + + # get base_width and base_height + video_settings = cl.get_video_settings() + base_width = video_settings.base_width + base_height = video_settings.base_height + + scene_name = stream_name # Name of the scene in OBS + + # get existing scenes + raw_list = cl.get_scene_list() + scene_list = [a['sceneName'] for a in raw_list.scenes] + + # if doesnt already exist create scene with this name + if scene_name not in scene_list: + cl.create_scene(scene_name) + cl.set_current_program_scene(scene_name) + + input_name = scene_name + " - vlc playlist" # Name for the media source in OBS + + raw_list = cl.get_scene_item_list(scene_name) + scene_item_list = [a['sourceName'] for a in raw_list.scene_items] + + # if source doesnt already exist create source with this name + if input_name not in scene_item_list: + full_playlist_path = playlist_path + input_kind = "vlc_source" # Kind of input (VLC source) + input_settings = { + "playlist": [{"hidden": False,"selected": False,"value": full_playlist_path}] + } + scene_item_enabled = True # Set to True to enable the scene item + # Create the VLC media source input + settings = cl.create_input(scene_name, input_name, input_kind, input_settings, scene_item_enabled) + + # transform -> fit to screen + scene_item_id = cl.get_scene_item_id(scene_name, input_name, offset=None).scene_item_id + cl.set_scene_item_transform(scene_name, scene_item_id, {'boundsWidth': base_width, 'boundsHeight': base_height, 'boundsType': 'OBS_BOUNDS_SCALE_INNER'}) + + + # Start the stream + logging.info("Starting stream with playlist: {}".format(playlist_path)) + current_playlist = playlist_path + start_time = datetime.now() + schedule = generate_schedule(start_time,playlist_path) + cl.start_stream() + stream_active = True + + except Exception as e: + logging.error(f"OBS Error: {e}") + finally: + cl.disconnect() + +def stop_stream(): + global stream_active + try: + # Initialize the OBS WebSocket client using ReqClient (connects automatically) + cl = obs.ReqClient(host=host, port=port, password=password, timeout=3) + + # Stop the stream + logging.info("Stopping stream") + cl.stop_stream() + stream_active = False + + except Exception as e: + logging.error(f"OBS Error: {e}") + finally: + cl.disconnect() + +def scheduled_stream(): + while not stop_streaming.is_set(): + current_time = datetime.now() + for key, times in stream_schedule.items(): + start_time = datetime.strptime(times['start_time'], '%Y-%m-%dT%H:%M') + stop_time = datetime.strptime(times['stop_time'], '%Y-%m-%dT%H:%M') + playlist_path = times['playlist'] + stream_name = times['stream_name'] + + if start_time <= current_time < stop_time and not stream_active: + start_stream(playlist_path, stream_name) + elif current_time >= stop_time and stream_active: + stop_stream() + time.sleep(60) # Check every minute + +def load_schedule(): + global schedule + if os.path.exists(schedule_file): + with open(schedule_file, 'r') as f: + schedule = json.load(f) + +def save_schedule(): + with open(schedule_file, 'w') as f: + json.dump(schedule, f, indent=4) + +def get_obs_state(): + obs_state = { + 'obs_running': False, + 'streaming': False, + 'recording': False + } + try: + cl = obs.ReqClient(host=host, port=port, password=password, timeout=3) + obs_state.update({ + 'obs_running': True, + 'streaming': cl.get_stream_status().output_active, + 'recording': cl.get_record_status().output_active + }) + except Exception as e: + logging.error(f"Unexpected error: {e}") + return obs_state + +@app.route('/') +def index(): + load_schedule() + obs_state = get_obs_state() + return render_template('index.html', current_playlist=current_playlist, schedule=schedule, obs_state=obs_state) + +@app.route('/schedule', methods=['POST']) +def schedule_stream(): + stream_name = request.form.get('stream_name') + start_time = request.form.get('start_time') + stop_time = request.form.get('stop_time') + playlist_path = request.form.get('playlist') + program = parse_playlist(playlist_path) + timestamp = datetime.strptime(start_time, '%Y-%m-%dT%H:%M') + program = [{'title': x['title'], 'start_time': (timestamp + timedelta(seconds=x['start_time'])).strftime('%Y-%m-%dT%H:%M')} for x in program] + + key = f"{start_time}-{stop_time}" + stream_schedule[key] = { + 'stream_name': stream_name, + 'start_time': start_time, + 'stop_time': stop_time, + 'playlist': playlist_path, + 'program': program + } + schedule.append({ + 'stream_name': stream_name, + 'start_time': start_time, + 'stop_time': stop_time, + 'playlist': playlist_path, + 'program': program + }) + save_schedule() + print(url_for('index')) + return redirect(url_for('index')) + + +if __name__ == "__main__": + # Load existing schedule + load_schedule() + # Start the scheduled streaming thread + threading.Thread(target=scheduled_stream, daemon=True).start() + # Run the Flask app + app.run(host='0.0.0.0', port=5000) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..4651182 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +Flask +m3u8 +obsws-python \ No newline at end of file diff --git a/static/style.css b/static/style.css new file mode 100644 index 0000000..04b28a2 --- /dev/null +++ b/static/style.css @@ -0,0 +1,17 @@ +body { + font-family: Arial, sans-serif; +} +h1, h2 { + color: #333; +} +table { + width: 100%; + border-collapse: collapse; +} +table, th, td { + border: 1px solid black; +} +th, td { + padding: 8px; + text-align: left; +} diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..17382e7 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,61 @@ + + + + + + OBS Scheduler + + + +

OBS Scheduler

+ +

OBS Status

+

OBS Running: {{ 'Yes' if obs_state['obs_running'] else 'No' }}

+

Streaming: {{ 'Yes' if obs_state['streaming'] else 'No' }}

+

Current Playlist: {{ current_playlist }}

+ +

Schedule Stream

+
+ +

+ +

+ +

+ +

+ +
+ + +

Schedule

+ + + + + + + + + + + {% for item in schedule %} + + + + + + + + {% endfor %} + +
Stream NameStart TimeEnd TimePlaylist File
{{ item['stream_name'] }}{{ item['start_time'] }}{{ item['stop_time'] }}{{ item['playlist'] }}
program times +
    + {% for x in item['program'] %} +
  • {{ x['start_time'] }} - {{ x['title'] }}
  • + {% endfor %} +
+ +
+ +