557 lines
16 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|