Files
2026-02-21 16:45:37 +08:00

557 lines
16 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using Crosstales.Radio.Model;
using Crosstales.Radio.Model.Entry;
using Crosstales.Radio.Model.Enum;
using Crosstales.Radio.Util;
using UnityEngine;
namespace Crosstales.Radio.Provider
{
[ExecuteInEditMode]
public abstract class BaseRadioProvider : MonoBehaviour
{
[Header("General Settings")]
[Header("Load Behaviour")]
[Tooltip("Clears all existing stations on 'Load' (default: true).")]
public bool ClearStationsOnLoad = true;
[Tooltip("Calls 'Load' on Start (default: true).")]
public bool LoadOnStart = true;
[Tooltip("Calls 'Load' on Start in Editor (default: true).")]
public bool LoadOnStartInEditor = true;
protected List<string> coRoutines = new List<string>();
private List<RadioStation> stations = new List<RadioStation>(Constants.INITIAL_LIST_SIZE);
private bool loadedInEditor = true;
private const string extM3UIdentifier = "#EXTM3U";
private const string extInfIdentifier = "#EXTINF";
private const string fileIdentifier = "file";
private const string titleIdentifier = "title";
private const string lengthIdentifier = "length";
private static char[] splitCharEquals = new char[1] { '=' };
private static char[] splitCharText = new char[1] { ';' };
private static char[] splitCharColon = new char[1] { ':' };
private static char[] splitCharComma = new char[1] { ',' };
public abstract List<BaseRadioEntry> RadioEntries { get; }
public List<RadioStation> Stations
{
get
{
return stations;
}
protected set
{
stations = value;
}
}
public bool isReady
{
get
{
if (Helper.isEditorMode)
{
return loadedInEditor;
}
return coRoutines.Count == 0;
}
}
public virtual void Start()
{
if ((LoadOnStart && !Helper.isEditorMode) || (LoadOnStartInEditor && Helper.isEditorMode))
{
Load();
}
OnValidate();
}
public virtual void OnValidate()
{
foreach (BaseRadioEntry radioEntry in RadioEntries)
{
if (radioEntry == null)
{
continue;
}
if (!radioEntry.isInitalized)
{
radioEntry.Format = AudioFormat.MP3;
radioEntry.EnableSource = true;
radioEntry.isInitalized = true;
}
if (radioEntry.Bitrate <= 0)
{
radioEntry.Bitrate = Config.DEFAULT_BITRATE;
}
else
{
radioEntry.Bitrate = Helper.NearestBitrate(radioEntry.Bitrate, radioEntry.Format);
}
if (radioEntry.ChunkSize <= 0)
{
radioEntry.ChunkSize = Config.DEFAULT_CHUNKSIZE;
}
else if (radioEntry.ChunkSize > Config.MAX_CACHESTREAMSIZE)
{
radioEntry.ChunkSize = Config.MAX_CACHESTREAMSIZE;
}
if (radioEntry.BufferSize <= 0)
{
radioEntry.BufferSize = Config.DEFAULT_BUFFERSIZE;
continue;
}
if (radioEntry.Format == AudioFormat.MP3)
{
if (radioEntry.BufferSize < Config.DEFAULT_BUFFERSIZE / 4)
{
radioEntry.BufferSize = Config.DEFAULT_BUFFERSIZE / 4;
}
}
else if (radioEntry.Format == AudioFormat.OGG && radioEntry.BufferSize < 64)
{
radioEntry.BufferSize = 64;
}
if (radioEntry.BufferSize < radioEntry.ChunkSize)
{
radioEntry.BufferSize = radioEntry.ChunkSize;
}
else if (radioEntry.BufferSize > Config.MAX_CACHESTREAMSIZE)
{
radioEntry.BufferSize = Config.MAX_CACHESTREAMSIZE;
}
}
}
public void Load()
{
if (!Helper.isEditorMode)
{
init();
}
}
public void Save(string path)
{
if (!string.IsNullOrEmpty(path))
{
try
{
path = path.Replace(Constants.PREFIX_FILE, string.Empty);
using (StreamWriter streamWriter = new StreamWriter(path))
{
streamWriter.WriteLine("# Radio PRO 2.9.2");
streamWriter.WriteLine("# © 2015-2017 by crosstales LLC (https://www.crosstales.com)");
streamWriter.WriteLine("#");
streamWriter.WriteLine("# List of all radio stations from '" + GetType().Name + "'");
streamWriter.WriteLine("# Created: " + DateTime.Now.ToString("dd.MM.yyyy"));
streamWriter.WriteLine("# Name;Url;DataFormat;AudioFormat;Station (optional);Genres (optional);Bitrate (in kbit/s, optional);Rating (0-5, optional);Description (optional);ExcludeCodec (optional);ChunkSize (in KB, optional);BufferSize (in KB, optional)");
foreach (RadioStation station in Stations)
{
streamWriter.WriteLine(station.ToTextLine());
}
return;
}
}
catch (IOException ex)
{
Debug.LogError("Could not write file: " + ex);
return;
}
}
Debug.LogWarning("'path' was null or empty! Could not save the data!");
}
protected virtual void init()
{
if (ClearStationsOnLoad)
{
Stations.Clear();
}
}
protected IEnumerator loadWeb(string uid, RadioEntryURL entry, bool suppressDoubleStations = false)
{
if (!string.IsNullOrEmpty(entry.FinalURL))
{
using (WWW www = new WWW(entry.FinalURL))
{
do
{
yield return www;
}
while (!www.isDone);
if (string.IsNullOrEmpty(www.error) && !string.IsNullOrEmpty(www.text))
{
List<string> list = Helper.SplitStringToLines(www.text);
yield return null;
if (list.Count > 0)
{
if (entry.DataFormat == DataFormatURL.M3U)
{
fillStationsFromM3U(list, entry, entry.ReadNumberOfStations, suppressDoubleStations);
}
else if (entry.DataFormat == DataFormatURL.PLS)
{
fillStationsFromPLS(list, entry, entry.ReadNumberOfStations, suppressDoubleStations);
}
else if (entry.DataFormat == DataFormatURL.Text)
{
fillStationsFromText(list, entry, entry.ReadNumberOfStations, suppressDoubleStations);
}
else
{
Debug.LogWarning("Not implemented!");
}
}
else
{
Debug.LogWarning(string.Concat(entry, " - URL: '", entry.FinalURL, "' does not contain any active radio stations!"));
}
}
else
{
Debug.LogWarning(string.Concat(entry, " - Could not load source: '", entry.FinalURL, "'", Environment.NewLine, www.error, Environment.NewLine, "Did you set the correct 'URL'?"));
}
}
}
else
{
Debug.LogWarning(string.Concat(entry, ": 'URL' is null or empty!", Environment.NewLine, "Please add a valid URL."));
}
coRoutines.Remove(uid);
}
protected IEnumerator loadResource(string uid, RadioEntryResource entry, bool suppressDoubleStations = false)
{
if (entry.Resource != null)
{
List<string> list = Helper.SplitStringToLines(entry.Resource.text);
yield return null;
if (list.Count > 0)
{
if (entry.DataFormat == DataFormatResource.M3U)
{
fillStationsFromM3U(list, entry, entry.ReadNumberOfStations, suppressDoubleStations);
}
else if (entry.DataFormat == DataFormatResource.PLS)
{
fillStationsFromPLS(list, entry, entry.ReadNumberOfStations, suppressDoubleStations);
}
else if (entry.DataFormat == DataFormatResource.Text)
{
fillStationsFromText(list, entry, entry.ReadNumberOfStations, suppressDoubleStations);
}
else
{
Debug.LogWarning("Not implemented!");
}
}
else
{
Debug.LogWarning(string.Concat(entry, " - Resource: '", entry.Resource, "' does not contain any active radio stations!"));
}
}
else
{
Debug.LogWarning(string.Concat(entry, ": resource field 'Resource' is null or empty!", Environment.NewLine, "Please add a valid resource."));
}
coRoutines.Remove(uid);
}
protected IEnumerator loadShoutcast(string uid, RadioEntryShoutcast entry, bool suppressDoubleStations = false)
{
using (WWW www = new WWW(Constants.SHOUTCAST + entry.ShoutcastID.Trim()))
{
do
{
yield return www;
}
while (!www.isDone);
if (string.IsNullOrEmpty(www.error) && !string.IsNullOrEmpty(www.text))
{
List<string> list = Helper.SplitStringToLines(www.text);
yield return null;
if (list.Count > 0)
{
fillStationsFromPLS(list, entry, 1, suppressDoubleStations);
}
else
{
Debug.LogWarning(string.Concat(entry, " - Shoutcast-ID: '", entry.ShoutcastID, "' does not contain any active radio stations!"));
}
}
else
{
Debug.LogWarning(string.Concat(entry, " - Could not load Shoutcast-ID: '", entry.ShoutcastID, "'", Environment.NewLine, www.error, Environment.NewLine, "Did you set the correct 'Shoutcast-ID'?"));
}
}
coRoutines.Remove(uid);
}
protected void fillStationsFromM3U(List<string> list, BaseRadioEntry entry, int readNumberOfStations = 0, bool suppressDoubleStations = false)
{
int num = 0;
for (int i = 0; i < list.Count; i++)
{
string text = string.Empty;
string text2 = string.Empty;
string text3 = list[i].Trim();
if (i == 0 && !text3.CTEquals("#EXTM3U"))
{
Debug.LogWarning("File is not in the M3U-format!");
break;
}
if (text3.CTContains("#EXTM3U"))
{
continue;
}
if (text3.CTContains("#EXTINF"))
{
string[] array = text3.Split(splitCharColon, StringSplitOptions.RemoveEmptyEntries);
if (array.Length > 1)
{
string[] array2 = array[1].Split(splitCharComma, StringSplitOptions.RemoveEmptyEntries);
if (array2.Length > 1)
{
text2 = array2[1];
}
}
if (i + 1 < list.Count)
{
i++;
text3 = list[i];
text = text3;
}
}
else if (!string.IsNullOrEmpty(text3))
{
text = text3;
}
if (string.IsNullOrEmpty(text))
{
continue;
}
RadioStation radioStation = new RadioStation(entry.ForceName ? entry.Name : ((!string.IsNullOrEmpty(text2)) ? text2.Trim() : entry.Name), text.Trim(), (entry.Format != AudioFormat.UNKNOWN) ? entry.Format : Helper.AudioFormatFromString(text), entry.Station, entry.Genres.ToLower(), entry.Bitrate, entry.Rating, entry.Description, entry.Icon, entry.ChunkSize, entry.BufferSize, entry.ExcludedCodec);
if (!Stations.Contains(radioStation))
{
Stations.Add(radioStation);
num++;
if (Constants.DEV_DEBUG)
{
Debug.Log("Station added: " + radioStation);
}
if (readNumberOfStations == num)
{
break;
}
}
else if (!suppressDoubleStations)
{
Debug.LogWarning(string.Concat("Station already added: '", entry, "'"));
}
}
}
protected void fillStationsFromPLS(List<string> list, BaseRadioEntry entry, int readNumberOfStations = 0, bool suppressDoubleStations = false)
{
int num = 0;
int num2 = 0;
while (num2 < list.Count)
{
string text = list[num2].Trim();
if (num2 == 0 && !text.CTEquals("[playlist]"))
{
Debug.LogWarning("File is not in the PLS-format!");
break;
}
string empty = string.Empty;
string text2 = string.Empty;
if (text.CTContains("file"))
{
string[] array = text.Split(splitCharEquals, StringSplitOptions.RemoveEmptyEntries);
if (array.Length > 1)
{
empty = array[1];
if (num2 + 1 < list.Count)
{
num2++;
text = list[num2];
if (text.CTContains("title"))
{
string[] array2 = text.Split(splitCharEquals, StringSplitOptions.RemoveEmptyEntries);
if (array2.Length > 1)
{
text2 = array2[1];
num2++;
}
}
}
if (string.IsNullOrEmpty(empty))
{
continue;
}
RadioStation radioStation = new RadioStation(entry.ForceName ? entry.Name : ((!string.IsNullOrEmpty(text2)) ? text2.Trim() : entry.Name), empty.Trim(), (entry.Format != AudioFormat.UNKNOWN) ? entry.Format : Helper.AudioFormatFromString(empty), entry.Station, entry.Genres.ToLower(), entry.Bitrate, entry.Rating, entry.Description, entry.Icon, entry.ChunkSize, entry.BufferSize, entry.ExcludedCodec);
if (!Stations.Contains(radioStation))
{
Stations.Add(radioStation);
num++;
if (Constants.DEV_DEBUG)
{
Debug.Log("Station added: " + radioStation);
}
if (readNumberOfStations == num)
{
break;
}
}
else if (!suppressDoubleStations)
{
Debug.LogWarning(string.Concat("Station already added: '", entry, "'"));
}
}
else
{
Debug.LogWarning(string.Concat(entry, ": No URL found for 'file': ", text));
}
}
else
{
num2++;
}
}
}
protected void fillStationsFromText(List<string> list, BaseRadioEntry entry, int readNumberOfStations = 0, bool suppressDoubleStations = false)
{
int num = 0;
foreach (string item in list)
{
string[] array = item.Split(splitCharText, StringSplitOptions.None);
if (array.Length >= 4 && array.Length <= 12)
{
RadioStation radioStation = new RadioStation(entry.ForceName ? entry.Name : ((!string.IsNullOrEmpty(array[0])) ? array[0].Trim() : entry.Name), array[1].Trim(), Helper.AudioFormatFromString(array[3].Trim()));
int result = entry.Bitrate;
float result2 = entry.Rating;
int result3 = entry.ChunkSize;
int result4 = entry.BufferSize;
if (array.Length >= 5)
{
radioStation.Station = array[4].Trim();
}
if (array.Length >= 6)
{
radioStation.Genres = array[5].Trim().ToLower();
}
if (array.Length >= 7)
{
radioStation.Bitrate = ((!int.TryParse(array[6].Trim(), out result)) ? entry.Bitrate : result);
}
if (array.Length >= 8)
{
radioStation.Rating = ((!float.TryParse(array[7].Trim(), out result2)) ? entry.Rating : result2);
}
if (array.Length >= 9)
{
radioStation.Description = array[8].Trim();
}
if (array.Length >= 10)
{
radioStation.ExcludedCodec = Helper.AudioCodecFromString(array[9].Trim());
}
if (array.Length >= 11)
{
radioStation.ChunkSize = ((!int.TryParse(array[10].Trim(), out result3)) ? entry.ChunkSize : result3);
}
if (array.Length == 12)
{
radioStation.BufferSize = ((!int.TryParse(array[11].Trim(), out result4)) ? entry.BufferSize : result4);
}
if (radioStation.Format == AudioFormat.OGG && radioStation.BufferSize < 64)
{
if (Config.DEBUG)
{
Debug.Log("Adjusted buffer size: " + radioStation);
}
radioStation.BufferSize = 64;
}
num++;
if (array[2].CTEquals("stream"))
{
if (!Stations.Contains(radioStation))
{
Stations.Add(radioStation);
if (Config.DEBUG)
{
Debug.Log("Station added: " + radioStation);
}
}
else if (!suppressDoubleStations)
{
Debug.LogWarning(string.Concat("Station already added: '", entry, "'"));
}
}
else if (array[2].CTContains("pls"))
{
if (!Helper.isEditorMode)
{
StartCoroutine(loadWeb(addCoRoutine(), new RadioEntryURL(radioStation, array[1].Trim(), DataFormatURL.PLS, 1)));
}
}
else if (array[2].CTContains("m3u"))
{
if (!Helper.isEditorMode)
{
StartCoroutine(loadWeb(addCoRoutine(), new RadioEntryURL(radioStation, array[1].Trim(), DataFormatURL.M3U, 1)));
}
}
else if (array[2].CTContains("shoutcast"))
{
if (!Helper.isEditorMode)
{
StartCoroutine(loadShoutcast(addCoRoutine(), new RadioEntryShoutcast(radioStation, array[1].Trim())));
}
}
else
{
Debug.LogWarning(string.Concat("Could not determine URL for station: '", entry, "'", Environment.NewLine, item));
num--;
}
if (readNumberOfStations == num)
{
break;
}
}
else
{
Debug.LogWarning(string.Concat("Invalid station description: '", entry, "''", Environment.NewLine, item));
}
}
}
protected string addCoRoutine()
{
string text = Guid.NewGuid().ToString();
coRoutines.Add(text);
return text;
}
}
}