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 coRoutines = new List(); private List stations = new List(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 RadioEntries { get; } public List 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 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 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 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 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 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 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; } } }