/// <summary> /// We need to make sure that the url that we are trying to treat as a file /// lies below the document root of the http server so that people can't grab /// random files off your computer while this is running. /// </summary> public void writeURL() { try { // first check if the request is actually authenticated IPEndPoint AC_endpoint = (IPEndPoint)s.RemoteEndPoint; if (!HTTPAuthProcessor.AllowedToAccessThisServer(AC_endpoint.Address)) { // now give the user a 403 and break... writeForbidden(); ns.Flush(); return; } querystring = ""; url = original_url; ConsoleOutputLogger.WriteLine("internal HTTP: " + HTTPAuthProcessor.IPtoUsername(AC_endpoint.Address.ToString()) + " - " + url); if (url.StartsWith("/vcr/")) { #region VCR request // remove the /vcr/ stuff url = url.Remove(0,5); // set this to true when implementing and reaching a new method bool method_found = false; // check which function is called #region AddSearchterm if (url.ToUpper().StartsWith("ADDSEARCHTERM")) { method_found = true; url = url.Remove(0, 14); // let's check for the category // TODO: better Querystring Handling!!!! string[] splitted = url.Split('&'); string categoryname = ""; string newsearchterm = ""; string[] splitted2 = splitted[0].Split('='); string[] splitted3 = splitted[1].Split('='); if (splitted2[0].ToUpper() == "CATEGORY") { categoryname = splitted2[1]; } if (splitted3[0].ToUpper() == "NEW_TERM") { newsearchterm = splitted3[1]; } if ((newsearchterm != "") & (categoryname != "")) { string Output; if (HTTPServer.vcr_scheduler.Category_Processor.AddSearchTerm(HttpUtility.UrlDecode(categoryname, Encoding.UTF7), HttpUtility.UrlDecode(newsearchterm, Encoding.UTF7))) { // check if there's already a category with that name Output = ForwardToPage("Searchterm " + HttpUtility.UrlDecode(newsearchterm, Encoding.UTF7) + " successfully added to the category " + HttpUtility.UrlDecode(categoryname, Encoding.UTF7), "/editcategory_step3.html?category="+categoryname); } else { Output = ForwardToPage("Error: Searchterm not added...", "/editcategory_step3.html?category=" + categoryname); } byte[] buffer = new UnicodeEncoding().GetBytes(Output); int left = new UnicodeEncoding().GetByteCount(Output); writeSuccess(left); ns.Write(buffer, 0, left); ns.Flush(); } // if not..well forget about it } #endregion #region DelSearchterm if (url.ToUpper().StartsWith("DELSEARCHTERM")) { method_found = true; url = url.Remove(0, 14); // let's check for the category // TODO: better Querystring Handling!!!! string[] splitted = url.Split('&'); string categoryname = ""; string searchterm = ""; string[] splitted2 = splitted[0].Split('='); string[] splitted3 = splitted[1].Split('='); if (splitted2[0].ToUpper() == "CATEGORY") { categoryname = splitted2[1]; } if (splitted3[0].ToUpper() == "TERM") { searchterm = splitted3[1]; } if ((searchterm != "") & (categoryname != "")) { string Output; if (HTTPServer.vcr_scheduler.Category_Processor.DelSearchTerm(HttpUtility.UrlDecode(categoryname, Encoding.UTF7), HttpUtility.UrlDecode(searchterm, Encoding.UTF7))) { // check if there's already a category with that name Output = ForwardToPage("Searchterm " + HttpUtility.UrlDecode(searchterm, Encoding.UTF7) + " successfully deleted from category " + HttpUtility.UrlDecode(categoryname, Encoding.UTF7), "/editcategory_step4.html?category=" + categoryname); } else { Output = ForwardToPage("Error: Searchterm not deleted...", "/editcategory_step4.html?category=" + categoryname); } byte[] buffer = new UnicodeEncoding().GetBytes(Output); int left = new UnicodeEncoding().GetByteCount(Output); writeSuccess(left); ns.Write(buffer, 0, left); ns.Flush(); } // if not..well forget about it } #endregion #region AddCategory if (url.ToUpper().StartsWith("ADDCATEGORY")) { method_found = true; url = url.Remove(0, 12); string[] splitted = url.Split('='); if (splitted[0].ToUpper() == "CATEGORYNAME") { string Output; // check if there's already a category with that name // it's UTF7 in this case because it comes directly from the browser... if (HTTPServer.vcr_scheduler.Category_Processor.AddCategory(HttpUtility.UrlDecode(splitted[1], Encoding.UTF7))) { Output = ForwardToPage("Category " + HttpUtility.UrlDecode(splitted[1], Encoding.UTF7) + " successfully added..", "/addcategory.html"); } else { // failed Output = ForwardToPage("Error: Category not added...", "/addcategory.html"); } byte[] buffer = new UnicodeEncoding().GetBytes(Output); int left = new UnicodeEncoding().GetByteCount(Output); writeSuccess(left); ns.Write(buffer, 0, left); ns.Flush(); } } #endregion #region DelCategory // rewritten addrecording/deleterecording... if (url.ToUpper().StartsWith("DELCATEGORY")) { method_found = true; url = url.Remove(0, 12); string[] splitted = url.Split('='); if (splitted[0].ToUpper() == "DELCATEGORY") { string Output; // it's UTF7 in this case because it comes directly from the browser... if (HTTPServer.vcr_scheduler.Category_Processor.DelCategory(HttpUtility.UrlDecode(splitted[1], Encoding.UTF7))) { Output = ForwardToPage("Category " + HttpUtility.UrlDecode(splitted[1], Encoding.UTF7) + " successfully deleted..", "/delcategory.html"); } else { // failed Output = ForwardToPage("Error: Category not found...", "/delcategory.html"); } byte[] buffer = new UnicodeEncoding().GetBytes(Output); int left = new UnicodeEncoding().GetByteCount(Output); writeSuccess(left); ns.Write(buffer, 0, left); ns.Flush(); } } #endregion #region ManageRecording // rewritten addrecording/deleterecording... if (url.ToUpper().StartsWith("MANAGERECORDING")) { url = url.Remove(0, 16); string[] parameterlist = url.Split('&'); // what type of function are we calling now? // fix the encoding; UTF8 that is for (int i = 0; i < parameterlist.Length; i++) { string[] split_parameter = HttpUtility.UrlDecode(parameterlist[i], Encoding.UTF8).Split('='); if (split_parameter[0].ToUpper() == "TYPE") { if (split_parameter[1].ToUpper() == "DEL") { url = "DELETERECORDING/" + url; break; } if (split_parameter[1].ToUpper() == "ADD") { url = "ADDRECORDING/" + url; break; } } } } #endregion #region AddRecording if (url.ToUpper().StartsWith("ADDRECORDING")) { #region CheckAuthentification if (!HTTPAuthProcessor.AllowedToCreateRecordings(AC_endpoint.Address)) { // now give the user a 403 and break... writeForbidden(); ns.Flush(); return; } #endregion method_found = true; url = url.Remove(0, 13); string[] parameterlist = url.Split('&'); // fix the encoding; UTF8 that is for (int i = 0; i < parameterlist.Length; i++) { parameterlist[i] = HttpUtility.UrlDecode(parameterlist[i], Encoding.UTF8); } // instantiate new Recording Recording newTimer = new Recording(); #region data'n'stuff int s_date = 0, s_month = 0, s_year = 0, s_hour = 0, s_minute = 0, e_date = 0, e_month = 0, e_year = 0, e_hour = 0, e_minute = 0; #endregion // TODO: add some try..catch thingies.. foreach (string Parameter in parameterlist) { try { string[] split_parameter = Parameter.Split('='); // Zwischenergebniss: s_date=1&s_month=8&s_year=2006&s_hour=12&s_minute=12&e_date=1 // &e_month=8&e_year=2006&e_hour=12&e_minute=12&name=1asdfasdfasf&ch=25 switch (split_parameter[0].ToUpper()) { case "S_DATE": s_date = Convert.ToInt32(split_parameter[1]); break; case "S_MONTH": s_month = Convert.ToInt32(split_parameter[1]); break; case "S_YEAR": s_year = Convert.ToInt32(split_parameter[1]); break; case "S_HOUR": s_hour = Convert.ToInt32(split_parameter[1]); break; case "S_MINUTE": s_minute = Convert.ToInt32(split_parameter[1]); break; case "E_DATE": e_date = Convert.ToInt32(split_parameter[1]); break; case "E_MONTH": e_month = Convert.ToInt32(split_parameter[1]); break; case "E_YEAR": e_year = Convert.ToInt32(split_parameter[1]); break; case "E_HOUR": e_hour = Convert.ToInt32(split_parameter[1]); break; case "E_MINUTE": e_minute = Convert.ToInt32(split_parameter[1]); break; case "NAME": newTimer.Recording_Name = HttpUtility.UrlDecode(split_parameter[1], Encoding.UTF8); // UTF-8 Decoding.. break; case "CH": newTimer.Channel = HttpUtility.UrlDecode(split_parameter[1], Encoding.UTF8);// UTF-8 Decoding.. break; case "REPEAT": if (split_parameter[1].ToUpper() == "DAILY") newTimer.isDaily = true; if (split_parameter[1].ToUpper() == "WEEKLY") newTimer.isWeekly = true; if (split_parameter[1].ToUpper() == "MONTHLY") newTimer.isMonthly = true; break; case "MO": if (split_parameter[1].ToUpper() == "ON") newTimer.Week[0] = true; break; case "TU": if (split_parameter[1].ToUpper() == "ON") newTimer.Week[1] = true; break; case "WE": if (split_parameter[1].ToUpper() == "ON") newTimer.Week[2] = true; break; case "TH": if (split_parameter[1].ToUpper() == "ON") newTimer.Week[3] = true; break; case "FR": if (split_parameter[1].ToUpper() == "ON") newTimer.Week[4] = true; break; case "SA": if (split_parameter[1].ToUpper() == "ON") newTimer.Week[5] = true; break; case "SU": if (split_parameter[1].ToUpper() == "ON") newTimer.Week[6] = true; break; case "HOLD": newTimer.HoldingTime = Convert.ToInt32(split_parameter[1]); break; case "IA": if (split_parameter[1].ToUpper() == "ON") newTimer.isAutomaticEPGRecording = true; break; case "AK": newTimer.AutomaticEPGRecordingKeywords.Add(HttpUtility.UrlDecode(split_parameter[1], Encoding.UTF8)); break; case "ARL": newTimer.AutomaticRecordingLength = Convert.ToInt32(split_parameter[1]); break; default: // alles andere interessiert uns nicht break; } } catch (Exception e) { ConsoleOutputLogger.WriteLine("AddRecording: "+e.Message); } } // TODO: add more checks for name+channel // we're trying to map the given name to a number... int ChannelNumber = ChannelAndStationMapper.Name2Number(newTimer.Channel); if (ChannelNumber != -1) { ConsoleOutputLogger.WriteLine("Apparently the channel " + newTimer.Channel + " has the number " + Convert.ToString(ChannelNumber)); // we found something... newTimer.Channel = Convert.ToString(ChannelNumber); } // try to map the IP to an username newTimer.createdby = HTTPAuthProcessor.IPtoUsername(AC_endpoint.Address.ToString()); ConsoleOutputLogger.WriteLine("Apparently the username is " + newTimer.createdby); // check if HoldingTime isn't already set... if (newTimer.HoldingTime == 0) { newTimer.HoldingTime = HTTPAuthProcessor.GetAccordingHoldingTime(AC_endpoint.Address.ToString()); } ConsoleOutputLogger.WriteLine("Apparently this users HoldingTime is " + newTimer.HoldingTime); // TODO: check if there are enough information given to actually create that timer try { if ((s_year + s_month + s_date != 0)) { newTimer.StartsAt = new DateTime(s_year, s_month, s_date, s_hour, s_minute, 0); newTimer.EndsAt = new DateTime(e_year, e_month, e_date, e_hour, e_minute, 0); } else { if (s_hour+s_minute+e_minute+e_hour != 0) { newTimer.StartsAt = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, s_hour, s_minute, 0); newTimer.EndsAt = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, e_hour, e_minute, 0); } else { newTimer.StartsAt = new DateTime(); newTimer.EndsAt = new DateTime(); } } } catch (Exception e) { ConsoleOutputLogger.WriteLine("AddRecording: " + e.Message); } //newTimer.Recording_Filename = newTimer.Recording_ID.ToString(); // TODO: check for other timers that could possibly concur... lock (HTTPServer.vcr_scheduler.Recordings.SyncRoot) { HTTPServer.vcr_scheduler.Recordings.Add(newTimer.Recording_ID, newTimer); } // tell the client that everything went fine... string Output = ForwardToIndexHTML("New Timer created..."); byte[] buffer = new UnicodeEncoding().GetBytes(Output); int left = new UnicodeEncoding().GetByteCount(Output); writeSuccess(left); ns.Write(buffer, 0, left); ns.Flush(); // Save the new Configuration... HTTPServer.Configuration.SaveSettings(); } #endregion #region DeleteRecording if (url.ToUpper().StartsWith("DELETERECORDING")) { // TODO: implement correct manage-recording version... method_found = true; // remove the /vcr/deleterecording? stuff url = url.Remove(0, 16); string[] splitted = url.Split('='); if (splitted[0].ToUpper() == "ID") { Guid delete_entry_id = Guid.NewGuid(); string recording_name = ""; string recording_username = ""; lock (HTTPServer.vcr_scheduler.Recordings.SyncRoot) { foreach (Recording recording_entry in HTTPServer.vcr_scheduler.Recordings.Values) { string original = recording_entry.Recording_ID.ToString(); if (original == (splitted[1])) { recording_name = recording_entry.Recording_Name; delete_entry_id = recording_entry.Recording_ID; recording_username = recording_entry.createdby; } } } #region CheckAuthentification if (!HTTPAuthProcessor.AllowedToDeleteRecordings(AC_endpoint.Address,recording_username)) { // now give the user a 403 and break... writeForbidden(); ns.Flush(); return; } #endregion bool deleted = true; try { lock (HTTPServer.vcr_scheduler.Recordings.SyncRoot) { HTTPServer.vcr_scheduler.Recordings.Remove(delete_entry_id); } } catch (Exception) { // failed deleted = false; } // tell the client that everything went fine... string Output; if (deleted) Output = ForwardToPage("Recording: " + recording_name + " successfully deleted...","/index.html"); else Output = ForwardToPage("Error: Recording not deleted...","/index.html"); byte[] buffer = new UnicodeEncoding().GetBytes(Output); int left = new UnicodeEncoding().GetByteCount(Output); writeSuccess(left); ns.Write(buffer, 0, left); ns.Flush(); // Save the new Configuration... HTTPServer.Configuration.SaveSettings(); } else { string[] parameterlist = url.Split('&'); // fix the encoding; UTF8 that is for (int i = 0; i < parameterlist.Length; i++) { parameterlist[i] = HttpUtility.UrlDecode(parameterlist[i], Encoding.UTF8); } // instantiate new Recording Recording newTimer = new Recording(); #region data'n'stuff int s_date = 0, s_month = 0, s_year = 0, s_hour = 0, s_minute = 0, e_date = 0, e_month = 0, e_year = 0, e_hour = 0, e_minute = 0; #endregion // find some information to identify the recording and delete it... try { foreach (string Parameter in parameterlist) { string[] split_parameter = Parameter.Split('='); // Zwischenergebniss: s_date=1&s_month=8&s_year=2006&s_hour=12&s_minute=12&e_date=1 // &e_month=8&e_year=2006&e_hour=12&e_minute=12&name=1asdfasdfasf&ch=25 switch (split_parameter[0].ToUpper()) { case "S_DATE": s_date = Convert.ToInt32(split_parameter[1]); break; case "S_MONTH": s_month = Convert.ToInt32(split_parameter[1]); break; case "S_YEAR": s_year = Convert.ToInt32(split_parameter[1]); break; case "S_HOUR": s_hour = Convert.ToInt32(split_parameter[1]); break; case "S_MINUTE": s_minute = Convert.ToInt32(split_parameter[1]); break; case "E_DATE": e_date = Convert.ToInt32(split_parameter[1]); break; case "E_MONTH": e_month = Convert.ToInt32(split_parameter[1]); break; case "E_YEAR": e_year = Convert.ToInt32(split_parameter[1]); break; case "E_HOUR": e_hour = Convert.ToInt32(split_parameter[1]); break; case "E_MINUTE": e_minute = Convert.ToInt32(split_parameter[1]); break; case "NAME": newTimer.Recording_Name = HttpUtility.UrlDecode(split_parameter[1], Encoding.UTF8); // UTF-8 Decoding.. break; case "CH": newTimer.Channel = HttpUtility.UrlDecode(split_parameter[1], Encoding.UTF8);// UTF-8 Decoding.. break; default: // alles andere interessiert uns nicht break; } } // we're trying to map the given name to a number... int ChannelNumber = ChannelAndStationMapper.Name2Number(newTimer.Channel); if (ChannelNumber != -1) { // we found something... newTimer.Channel = Convert.ToString(ChannelNumber); } try { newTimer.StartsAt = new DateTime(s_year, s_month, s_date, s_hour, s_minute, 0); newTimer.EndsAt = new DateTime(e_year, e_month, e_date, e_hour, e_minute, 0); } catch (Exception e) { ConsoleOutputLogger.WriteLine("Deleterecording: " + e.Message); } Guid tobedeletedID = Guid.NewGuid(); bool foundanID = false; string recording_username = ""; // now we search for a corresponding recording ... lock (HTTPServer.vcr_scheduler.Recordings.SyncRoot) { foreach (Recording timer in HTTPServer.vcr_scheduler.Recordings.Values) { if (timer.Channel == newTimer.Channel) { if (timer.StartsAt == newTimer.StartsAt) { if (timer.EndsAt == newTimer.EndsAt) { // found it...now save the key tobedeletedID = timer.Recording_ID; foundanID = true; recording_username = timer.createdby; break; } } } } if (foundanID) { #region CheckAuthentification if (!HTTPAuthProcessor.AllowedToDeleteRecordings(AC_endpoint.Address, recording_username)) { // now give the user a 403 and break... writeForbidden(); ns.Flush(); return; } #endregion ConsoleOutputLogger.WriteLine("Found Recording ... deleting"); HTTPServer.vcr_scheduler.Recordings.Remove(tobedeletedID); } } // tell the client that everything went fine... string Output; if (foundanID) Output = ForwardToPage("Recording successfully deleted...","/index.html"); else Output = ForwardToPage("Error: Recording not deleted...","index.html"); byte[] buffer = new UnicodeEncoding().GetBytes(Output); int left = new UnicodeEncoding().GetByteCount(Output); writeSuccess(left); ns.Write(buffer, 0, left); ns.Flush(); } catch (Exception e) { ConsoleOutputLogger.WriteLine("Deleterecording(Findrecording): " + e.Message); writeFailure(); } } } #endregion #region RemoveRecordingFile if (url.ToUpper().StartsWith("REMOVERECORDINGFILE")) { method_found = true; // remove the deleterecording? stuff url = url.Remove(0, 20); string[] splitted = url.Split('='); if (splitted[0].ToUpper() == "ID") { Guid delete_entry_id = Guid.NewGuid(); //String delete_entry_filename = ""; Recording tobedeletedrecording = null; lock (HTTPServer.vcr_scheduler.doneRecordings.SyncRoot) { foreach (Recording recording_entry in HTTPServer.vcr_scheduler.doneRecordings.Values) { string original = recording_entry.Recording_ID.ToString(); if (original == splitted[1]) { tobedeletedrecording = recording_entry; } } } bool deleted = true; #region CheckAuthentification if (!HTTPAuthProcessor.AllowedToDeleteRecordings(AC_endpoint.Address, tobedeletedrecording.createdby)) { // now give the user a 403 and break... writeForbidden(); ns.Flush(); return; } #endregion try { lock (HTTPServer.vcr_scheduler.doneRecordings.SyncRoot) { // remove file File.Delete(tobedeletedrecording.Recording_Filename); // remove entry in Hashtable HTTPServer.vcr_scheduler.doneRecordings.Remove(tobedeletedrecording.Recording_ID); } } catch (Exception) { // failed deleted = false; } // tell the client that everything went fine... string Output; if (deleted) Output = ForwardToPage("Recording: " + tobedeletedrecording.Recording_Name + " successfully deleted...","/recordings.html"); else Output = ForwardToPage("Error: Recording not deleted...", "/recordings.html"); byte[] buffer = new UnicodeEncoding().GetBytes(Output); int left = new UnicodeEncoding().GetByteCount(Output); writeSuccess(left); ns.Write(buffer, 0, left); ns.Flush(); // Remove the playlist file for the XBMC player if (deleted) { try { File.Delete(XBMCPlaylistFilesHelper.generatePlaylistFilename(tobedeletedrecording)); File.Delete(XBMCPlaylistFilesHelper.generateThumbnailFilename(tobedeletedrecording)); } catch { ConsoleOutputLogger.WriteLine("HTTP Server Exception: Could not delete the playlistfile for " + tobedeletedrecording.Recording_Name); } } // Save the new Configuration... HTTPServer.Configuration.SaveSettings(); } else writeFailure(); } #endregion #region Reset PlayPosition if (url.ToUpper().StartsWith("RESETRECORDING")) { method_found = true; // remove the deleterecording? stuff url = url.Remove(0, 15); string[] splitted = url.Split('='); if (splitted[0].ToUpper() == "ID") { Guid delete_entry_id = Guid.NewGuid(); lock (HTTPServer.vcr_scheduler.doneRecordings.SyncRoot) { foreach (Recording recording_entry in HTTPServer.vcr_scheduler.doneRecordings.Values) { string original = recording_entry.Recording_ID.ToString(); if (original == splitted[1]) { recording_entry.SetLastStopPosition(HTTPAuthProcessor.IPtoUsername(AC_endpoint.Address.ToString()),0); } } } string Output; Output = ForwardToPage("Recording PlayPosition was reset...", "/recordings.html"); byte[] buffer = new UnicodeEncoding().GetBytes(Output); int left = new UnicodeEncoding().GetByteCount(Output); writeSuccess(left); ns.Write(buffer, 0, left); ns.Flush(); // Save the new Configuration... HTTPServer.Configuration.SaveSettings(); } else writeFailure(); } #endregion if (!method_found) { // nothing to do... writeError(404, "No Method found"); } #endregion } else if (url.StartsWith("/rss/")) { #region XML RSS feed requests // remove the /rss/ stuff url = url.Remove(0, 5); #region Recordings Listing RSS if (url.ToUpper().StartsWith("RECORDED.XML")) { url = url.Remove(0, 12); System.IO.MemoryStream XMLStream = new System.IO.MemoryStream(); RSS.RSSChannel oRSSChannel = new RSS.RSSChannel("Recorded Listings",HTTPServer.Settings.HTTP_URL, "Displays all the recordings currently available for watching"); oRSSChannel.PubDate = System.DateTime.Now.ToString(); RSS.RSSImage oRSSImage = new RSS.RSSImage(HTTPServer.Settings.HTTP_URL + "/images/RSS_Logo_YAPS.png", HTTPServer.Settings.HTTP_URL, "YAPS VCR"); RSS.RSSRoot oRSSRoot = new RSS.RSSRoot(oRSSChannel,oRSSImage, XMLStream); RSS.RSSItem oRSSItem = null; // now go for a walk with all the Recordings... #region Sort the Recordings Listing List<Recording> sortedDoneRecordingList; lock (HTTPServer.vcr_scheduler.doneRecordings.SyncRoot) { // TODO: add ascending/descending setting + reimplement the sorting algorithm sortedDoneRecordingList = Sorter.SortRecordingTable(HTTPServer.vcr_scheduler.doneRecordings, false); } #endregion // TODO: maybe we can do this without foreach...faster; but because this is not critical function of YAPS...well low priority foreach (Recording recording in sortedDoneRecordingList) { oRSSItem = new RSS.RSSItem(recording.Recording_Name, HTTPServer.Settings.HTTP_URL + "/" + recording.Recording_ID.ToString()); oRSSItem.PubDate = recording.StartsAt.ToString(); oRSSItem.Comment = recording.Comment; // generate Category List for this recording oRSSItem.Categories = HTTPServer.vcr_scheduler.Category_Processor.RenderCategoryLine(HTTPServer.vcr_scheduler.Category_Processor.AutomaticCategoriesForRecording(recording), ','); oRSSRoot.Items.Add(oRSSItem); } RSS.RSSUtilities.PublishRSS(oRSSRoot); XMLStream.Seek(0, SeekOrigin.Begin); byte[] byteArray = new byte[XMLStream.Length]; int xmlcount = XMLStream.Read(byteArray, 0, Convert.ToInt32(XMLStream.Length)); writeSuccess(xmlcount,"text/xml"); ns.Write(byteArray, 0, xmlcount); ns.Flush(); XMLStream.Close(); } #endregion #endregion } else if (url.StartsWith("/dvb/")) { #region Streaming Request #region CheckAuthentification if (!HTTPAuthProcessor.AllowedToAccessLiveStream(AC_endpoint.Address)) { // now give the user a 403 and break... writeForbidden(); ns.Flush(); return; } #endregion // check if there is a MulticastProcessor Object for this channel VCRandStreaming HReq = new VCRandStreaming(false,null,HTTPServer); #region CheckAuthentification if (!HTTPAuthProcessor.AllowedToAccessLiveStream(AC_endpoint.Address)) { // now give the user a 403 and break... writeForbidden(); ns.Flush(); return; } #endregion try { HReq.HandleStreaming(url, this); while (!HReq.done) { Thread.Sleep(100); } HTTPServer.Settings.NumberOfPlayedLiveTV++; } catch (Exception e) { ConsoleOutputLogger.WriteLine("Streaming error: " + e.Message); try { writeFailure(); } catch (Exception) { ConsoleOutputLogger.WriteLine("Streaming error: client disconnected"); } } #endregion } else if (url.StartsWith("/xml/")) { #region Tuxbox Request #region CheckAuthentification if (!HTTPAuthProcessor.AllowedToAccessTuxbox(AC_endpoint.Address)) { // now give the user a 403 and break... writeForbidden(); ns.Flush(); return; } #endregion url = url.Remove(0,5); // set this to true when implementing and reaching a new method bool method_found = false; // check which function is called #region boxstatus if (url.ToUpper().StartsWith("BOXSTATUS")) { method_found = true; /* * <status> * <current_time>Sat Jul 23 23:03:17 2005</current_time> * <standby>OFF</standby> * <recording>OFF</recording> * <mode>0</mode> * <ip>192.168.0.10</ip> * </status> * * */ StringBuilder boxstatus = new StringBuilder(); // of course it would be possible to do this through serializers but it would be // much more hassle than this: boxstatus.AppendLine("<status>"); boxstatus.AppendLine("<current_time>"+DateTime.Now.ToString()+"</current_time>"); boxstatus.AppendLine("<standby>OFF</standby>"); boxstatus.AppendLine("<recording>OFF</recording>"); boxstatus.AppendLine("<mode>0</mode>"); boxstatus.AppendLine("<ip>"+HTTPServer.ipaddress.ToString()+"</ip>"); boxstatus.AppendLine("</status>"); byte[] buffer = new UTF8Encoding().GetBytes(boxstatus.ToString()); writeSuccess(buffer.Length, "text/xml"); ns.Write(buffer, 0, buffer.Length); ns.Flush(); } #endregion #region services if (url.ToUpper().StartsWith("SERVICES")) { method_found = true; System.IO.MemoryStream XMLStream = new System.IO.MemoryStream(); // TODO: add check if EPGProcessor is even instantiated try { List<tuxbox.bouquet> bouquets = new List<YAPS.tuxbox.bouquet>(); // add Currently Running events to the list... tuxbox.bouquet currentlyrunningbouquet = TuxboxProcessor.addCurrentlyRunningBouquet(HTTPServer.EPGProcessor); bouquets.Add(currentlyrunningbouquet); XmlRootAttribute xRoot = new XmlRootAttribute(); xRoot.ElementName = "bouquets"; xRoot.IsNullable = true; System.Xml.Serialization.XmlSerializer xmls = new XmlSerializer(bouquets.GetType(),xRoot); xmls.Serialize(XMLStream, bouquets); XMLStream.Seek(0, SeekOrigin.Begin); byte[] byteArray = new byte[XMLStream.Length]; int xmlcount = XMLStream.Read(byteArray, 0, Convert.ToInt32(XMLStream.Length)); writeSuccess(xmlcount, "text/xml"); ns.Write(byteArray, 0, xmlcount); ns.Flush(); } finally { XMLStream.Close(); } } #endregion #region streaminfo // Currently broken (!) if (url.ToUpper().StartsWith("STREAMINFO2")) { method_found = true; if (TuxboxProcessor.ZapToChannel != "") { // get the currently running event information for this channel... //EPG_Event_Entry currentlyrunningevent = TuxboxProcessor.getCurrentlyRunningEventOnChannel(TuxboxProcessor.ZapToChannel); /* * 480 * 576 * 997500 * 4:3 * 25 * joint stereo * */ // first of all get the info what's currently running on the ZapedToChannel StringBuilder streaminfo = new StringBuilder(); // TODO: add correct aspect ratio, resolution, bitrate streaminfo.AppendLine("768\n"); // vertical resolution streaminfo.AppendLine("576\n"); // horizontal resolution streaminfo.AppendLine("997500\n"); // bitrate streaminfo.AppendLine("4:3\n"); // aspect ratio streaminfo.AppendLine("25\n"); // frames per second streaminfo.AppendLine("joint stereo\n"); // audio information byte[] buffer = new UTF8Encoding().GetBytes(streaminfo.ToString()); writeSuccess(buffer.Length, "text/html"); ns.Write(buffer, 0, buffer.Length); ns.Flush(); } else { ConsoleOutputLogger.WriteLine("No ZappedTo Channel found. Please first do a /cgi-bin/ZapTo?Path="); writeFailure(); } } #endregion #region currentservicedata if (url.ToUpper().StartsWith("CURRENTSERVICEDATA")) { method_found = true; if (TuxboxProcessor.ZapToChannel != "") { // get the currently running event information for this channel... EPG_Event_Entry currentlyrunningevent = TuxboxProcessor.getCurrentlyRunningEventOnChannel(TuxboxProcessor.ZapToChannel,HTTPServer.EPGProcessor); System.IO.MemoryStream XMLStream = new System.IO.MemoryStream(); // TODO: add check if EPGProcessor is even instantiated if (currentlyrunningevent != null) { try { YAPS.tuxbox.currentservicedata CurrentServiceData_ = new YAPS.tuxbox.currentservicedata(); XmlRootAttribute xRoot = new XmlRootAttribute(); xRoot.ElementName = "currentservicedata"; xRoot.IsNullable = true; // TODO: this is default... implement later YAPS.tuxbox.channel channel = new YAPS.tuxbox.channel(); channel.Name = "Stereo"; channel.pid = "0x01"; channel.selected = 1; CurrentServiceData_.audio_channels.Add(channel); CurrentServiceData_.current_event.date = currentlyrunningevent.StartTime.ToShortDateString(); CurrentServiceData_.current_event.description = currentlyrunningevent.ShortDescription.Name; CurrentServiceData_.current_event.details = currentlyrunningevent.ShortDescription.Text; TimeSpan event_duration = currentlyrunningevent.EndTime - currentlyrunningevent.StartTime; CurrentServiceData_.current_event.duration = event_duration.Minutes.ToString(); CurrentServiceData_.current_event.start = currentlyrunningevent.StartTime.Ticks.ToString(); CurrentServiceData_.current_event.time = currentlyrunningevent.StartTime.ToShortTimeString(); CurrentServiceData_.next_event = CurrentServiceData_.current_event; CurrentServiceData_.service.name = TuxboxProcessor.ZapToChannel; CurrentServiceData_.service.reference = TuxboxProcessor.ZapToChannel; System.Xml.Serialization.XmlSerializer xmls = new XmlSerializer(CurrentServiceData_.GetType(), xRoot); xmls.Serialize(XMLStream, CurrentServiceData_); XMLStream.Seek(0, SeekOrigin.Begin); byte[] byteArray = new byte[XMLStream.Length]; int xmlcount = XMLStream.Read(byteArray, 0, Convert.ToInt32(XMLStream.Length)); writeSuccess(xmlcount, "text/xml"); ns.Write(byteArray, 0, xmlcount); ns.Flush(); } finally { XMLStream.Close(); } } else { ConsoleOutputLogger.WriteLine("There are no CurrentlyRunningEvents we know off. Check EPG!"); writeFailure(); } } else { ConsoleOutputLogger.WriteLine("No ZappedTo Channel found. Please first do a /cgi-bin/ZapTo?Path="); writeFailure(); } } #endregion #region boxinfo if (url.ToUpper().StartsWith("BOXINFO")) { method_found = true; System.IO.MemoryStream XMLStream = new System.IO.MemoryStream(); // TODO: add check if EPGProcessor is even instantiated try { XmlRootAttribute xRoot = new XmlRootAttribute(); xRoot.ElementName = "boxinfo"; xRoot.IsNullable = true; YAPS.tuxbox.boxinfo boxinfo = new YAPS.tuxbox.boxinfo(); boxinfo.disk = "none"; boxinfo.firmware = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(); boxinfo.fpfirmware = "n/a"; boxinfo.image.version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(); boxinfo.image.catalog = "http://www.technology-ninja.com"; boxinfo.image.comment = "Yet Another Proxy Server: UDP Multicast to TCP Unicast Proxy"; boxinfo.image.url = "http://www.technology-ninja.com"; boxinfo.manufacturer = "(C) 2006-2007 Daniel Kirstenpfad and the YAPS Team"; boxinfo.model = "YAPS"; boxinfo.processor = "n/a"; boxinfo.usbstick = "none"; boxinfo.webinterface = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(); System.Xml.Serialization.XmlSerializer xmls = new XmlSerializer(boxinfo.GetType(), xRoot); xmls.Serialize(XMLStream, boxinfo); XMLStream.Seek(0, SeekOrigin.Begin); byte[] byteArray = new byte[XMLStream.Length]; int xmlcount = XMLStream.Read(byteArray, 0, Convert.ToInt32(XMLStream.Length)); writeSuccess(xmlcount, "text/xml"); ns.Write(byteArray, 0, xmlcount); ns.Flush(); } finally { XMLStream.Close(); } } #endregion if (!method_found) { ConsoleOutputLogger.WriteLine("Tuxbox stuff is coming soon"); writeFailure(); } #endregion } else if (url.StartsWith("/cgi-bin/")) { bool method_found = false; // remove the start url = url.Remove(0, 9); #region Tuxbox Requests #region CheckAuthentification if (!HTTPAuthProcessor.AllowedToAccessTuxbox(AC_endpoint.Address)) { // now give the user a 403 and break... writeForbidden(); ns.Flush(); return; } #endregion #region zapTo if (url.ToUpper().StartsWith("ZAPTO?PATH=")) { method_found = true; url = url.Remove(0, 11); if (ChannelAndStationMapper.Name2Number(url) != -1) { TuxboxProcessor.ZapToChannel = url; String Output = "ok"; byte[] buffer = new UnicodeEncoding().GetBytes(Output); int left = new UnicodeEncoding().GetByteCount(Output); writeSuccess(left); ns.Write(buffer, 0, left); ns.Flush(); } else { ConsoleOutputLogger.WriteLine("Station not found, cannot zap to this channel: "+url); String Output = "error"; byte[] buffer = new UnicodeEncoding().GetBytes(Output); int left = new UnicodeEncoding().GetByteCount(Output); writeSuccess(left); ns.Write(buffer, 0, left); ns.Flush(); } } #endregion #endregion if (!method_found) { ConsoleOutputLogger.WriteLine("Tuxbox stuff is coming soon"); writeFailure(); } } else { #region File request (everything else...) #region default page if (url == "/") { url = "/index.html"; } #endregion // check if we have some querystring parameters if (url.Contains("?")) { // yes, remove everything after the ? from the url but save it to querystring querystring = url.Substring(url.IndexOf('?') + 1); url = url.Remove(url.IndexOf('?')); } // Replace the forward slashes with back-slashes to make a file name string filename = url.Replace('/', Path.DirectorySeparatorChar); //you have different path separators in unix and windows #region Update the Playcount (eventually) Recording currentlyPlaying = null; // HACK: eventually this breaks when the recording path becomes configurable...take care of that! try { // Strip first character (which should be the DirectorySeparator) string doneRecordingFilename = filename.Remove(0, 1); lock (HTTPServer.vcr_scheduler.doneRecordings) { // TODO: maybe we can do this without foreach...faster; but because this is not critical function of YAPS...well low priority foreach (Recording recording in HTTPServer.vcr_scheduler.doneRecordings.Values) if (recording.Recording_ID.ToString() == doneRecordingFilename) { #region CheckAuthentification if (!HTTPAuthProcessor.AllowedToAccessRecordings(AC_endpoint.Address)) { // now give the user a 403 and break... writeForbidden(); ns.Flush(); return; } // check if this recording is the Recording of another user //if (HTTPAuthProcessor.IPtoUsername(AC_endpoint.Address.ToString()).ToUpper() != recording.Username.ToUpper()) //{ // if (HTTPAuthProcessor.AllowedToAccessOthersRecordings(AC_endpoint.Address)) // { // // now give the user a 403 and break... // writeForbidden(); // ns.Flush(); // return; // } //} #endregion currentlyPlaying = recording; recording.PlayCount++; HTTPServer.Settings.NumberOfPlayedRecordings++; recording.LastTimePlayed = DateTime.Now; ConsoleOutputLogger.WriteLine("Increasing Playcount for Recording " + recording.Recording_Name); HTTPServer.Configuration.SaveSettings(); } } } catch (Exception e) { ConsoleOutputLogger.WriteLine("HTTP.UpdatePlayCount: " + e.Message); } #endregion try { // Construct a filename from the doc root and the filename FileInfo file = new FileInfo(docRootFile + filename); // Make sure they aren't trying in funny business by checking that the // resulting canonical name of the file has the doc root as a subset. filename = file.FullName; if (!filename.StartsWith(docRootFile.FullName)) { writeForbidden(); } else { FileStream fs = null; BufferedStream bs = null; long bytesSent = 0; bool resumed = false; try { if (filename.EndsWith(".log")) { // now give the user a 403 and break... writeForbidden(); ns.Flush(); } else if (filename.EndsWith(".html") | (filename.EndsWith(".htm"))) { ConsoleOutputLogger.WriteLine("[DEBUG] 1"); String Output = HTTPServer.Template_Processor.ProcessHTMLTemplate(filename, querystring, HTTPAuthProcessor.IPtoUsername(AC_endpoint.Address.ToString())); ConsoleOutputLogger.WriteLine("[DEBUG] 2"); //int left = new UnicodeEncoding().GetByteCount(Output); int left = new UTF8Encoding().GetByteCount(Output); writeSuccess(left, "text/html"); byte[] buffer = new UTF8Encoding().GetBytes(Output); //HttpServerUtility ut = new HttpServerUtility(); //ut.HtmlEncode(new UTF8Encoding().G //byte[] buffer = new UnicodeEncoding().GetBytes(Output); ns.Write(buffer, 0, left); ns.Flush(); } else { // Open the file for binary transfer fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); long left = file.Length; bool isThisARecordingRecording = false; // TODO: make the resuming behaviour for streamed recordings configurable!! if (currentlyPlaying != null) { //if (currentlyPlaying.LastStoppedPosition != 0) resumed = true; left = file.Length - currentlyPlaying.LastStopPosition(HTTPAuthProcessor.IPtoUsername(AC_endpoint.Address.ToString())); bytesSent = currentlyPlaying.LastStopPosition(HTTPAuthProcessor.IPtoUsername(AC_endpoint.Address.ToString())); writeSuccess(left, "video/mpeg2"); isThisARecordingRecording = true; } else { // TODO: it'll be nice to handle jpeg/... well known file extensions as different mime types in the future... #region different mime-type-handling switch (getFileExtension(filename)) { case ".css": writeSuccess(left, "text/css"); break; case ".gif": writeSuccess(left, "image/gif"); break; case ".png": writeSuccess(left, "image/png"); break; case ".jpg": writeSuccess(left, "image/jpeg"); break; case ".jpeg": writeSuccess(left, "image/jpeg"); break; default: // Write the content length and the success header to the stream; it's binary...so treat it as binary writeSuccess(left, "application/octet-stream"); break; } } #endregion // Copy the contents of the file to the stream, ensure that we never write // more than the content length we specified. Just in case the file somehow // changes out from under us, although I don't know if that is possible. bs = new BufferedStream(fs); left = file.Length; if (currentlyPlaying != null) bs.Seek(currentlyPlaying.LastStopPosition(HTTPAuthProcessor.IPtoUsername(AC_endpoint.Address.ToString())), SeekOrigin.Begin); // for performance reasons... String tUsername = HTTPAuthProcessor.IPtoUsername(AC_endpoint.Address.ToString()); int read; while (left > 0 && (read = bs.Read(bytes, 0, (int)Math.Min(left, bytes.Length))) != 0) { ns.Write(bytes, 0, read); bytesSent = bytesSent + read; left -= read; // check filesize; maybe when we're viewing while recording the filesize may change from time to time... left = file.Length; // set the already watched Size... if (isThisARecordingRecording) currentlyPlaying.SetLastStopPosition(tUsername,bytesSent); } ns.Flush(); bs.Close(); fs.Close(); // this happens when we're all done... if (currentlyPlaying != null) { currentlyPlaying.SetLastStopPosition(HTTPAuthProcessor.IPtoUsername(AC_endpoint.Address.ToString()), 0); // generate Thumbnail RecordingsThumbnail.CreateRecordingsThumbnail(currentlyPlaying, XBMCPlaylistFilesHelper.generateThumbnailFilename(currentlyPlaying)); HTTPServer.Configuration.SaveSettings(); } } } catch (Exception e) { ConsoleOutputLogger.WriteLine("writeURL: " + e.Message); try { writeFailure(); } catch (Exception) { ConsoleOutputLogger.WriteLine("writeURL.Result: connection lost to client"); } if (bs != null) bs.Close(); if (bs != null) fs.Close(); // TODO: make the behaviour configurable: What happens when the streaming did end...and the User restarts.. if (!resumed) { if (currentlyPlaying != null) { //currentlyPlaying.SetLastStopPosition(HTTPAuthProcessor.IPtoUsername(AC_endpoint.Address.ToString()), bytesSent); // generate Thumbnail RecordingsThumbnail.CreateRecordingsThumbnail(currentlyPlaying, XBMCPlaylistFilesHelper.generateThumbnailFilename(currentlyPlaying)); // Save it HTTPServer.Configuration.SaveSettings(); } } else { HTTPServer.Settings.NumberOfPlayedRecordings++; currentlyPlaying.SetLastStopPosition(HTTPAuthProcessor.IPtoUsername(AC_endpoint.Address.ToString()), 0); HTTPServer.Configuration.SaveSettings(); } } } } catch (Exception e) { ConsoleOutputLogger.WriteLine("HTTPProcessor.writeURL(): " + e.Message); writeFailure(); } #endregion } } catch(Exception e) { ConsoleOutputLogger.WriteLine("Unhandled http: " + e.Message); writeFailure(); } }
/// <summary> /// the actual Scheduler /// </summary> /// <param name="internal_http_processor_instance">we need this because the handler class needs it to get to the multicast and receiver lists...does</param> public void RecordingScheduler() { // tell the HTTP Server that we're up'n'running internal_http_server_object.vcr_scheduler = this; lock (doneRecordings.SyncRoot) { // check if we have some undone recordings in the queue that need to be set to "done"... foreach (Recording recording_entry in doneRecordings.Values) { if (recording_entry.CurrentlyRecording) { recording_entry.CurrentlyRecording = false; ConsoleOutputLogger.WriteLine("Obviously the recording " + recording_entry.Recording_Name + " did not finish properly."); } #region HACK: Create playlist files on launch... // normally commented section, use only of you know what you're doing /*using (StreamWriter sw = new StreamWriter(XBMCPlaylistFilesHelper.generatePlaylistFilename(recording_entry))) { // Add some text to the file. sw.Write(Settings.HTTP_URL + "/" + recording_entry.Recording_Filename); sw.Close(); } File.SetLastWriteTime(XBMCPlaylistFilesHelper.generatePlaylistFilename(recording_entry), recording_entry.EndsAt); File.SetCreationTime(XBMCPlaylistFilesHelper.generatePlaylistFilename(recording_entry), recording_entry.EndsAt);*/ #endregion } } ConsoleOutputLogger.WriteLine("VCR Scheduler up and running..."); // as long as we're not done, check for upcoming recordings... while ((true) && (!done)) { try { if (Recordings.Count > 0) { foreach (Recording recording_entry in Recordings.Values) { if (recording_entry.isAutomaticEPGRecording) { #region Automatic Recordings lock (internal_http_server_object.EPGProcessor.CurrentlyRunningEvents) { foreach (EPG_Event_Entry currentlyRunningEvent in internal_http_server_object.EPGProcessor.CurrentlyRunningEvents) { if (!currentlyRunningEvent.isRecorded) { // if a channel is set, only record if the new event is on that channel if (recording_entry.Channel != "") { if (recording_entry.Channel != ChannelAndStationMapper.Name2Number(ChannelAndStationMapper.ServiceID2Name(currentlyRunningEvent.Service)).ToString()) { //ConsoleOutputLogger.WriteLine(recording_entry.Channel+" != "+ChannelAndStationMapper.Name2Number(ChannelAndStationMapper.ServiceID2Name(currentlyRunningEvent.Service)).ToString()); continue; } } // 15.03.2008 - 10:07 Debug: 633411724556096250 (DateTime.Now) // 15.03.2008 - 10:07 Debug: 633411690000000000 (DateTime.StartTime) // if the start time is set, look if this is correct if (recording_entry.StartsAt.Ticks != 0) { DateTime newStartsAt = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, recording_entry.StartsAt.Hour, recording_entry.StartsAt.Minute, DateTime.Now.Second, 0); // if the recording hasn't even started yet if (newStartsAt.Ticks >= DateTime.Now.Ticks) { //ConsoleOutputLogger.WriteLine(newStartsAt.ToShortTimeString()+" >= "+DateTime.Now.ToShortTimeString()); continue; } } // if the end time is set, look if this is correct if (recording_entry.EndsAt.Ticks != 0) { DateTime newEndsAt = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, recording_entry.EndsAt.Hour, recording_entry.EndsAt.Minute, DateTime.Now.Second, 0); if (newEndsAt.Ticks <= DateTime.Now.Ticks) { //ConsoleOutputLogger.WriteLine(newEndsAt.ToShortTimeString() + " <= " + DateTime.Now.ToShortTimeString()); continue; } } // check if the time is right... #region only handle this event if it's not already recorded... foreach (String Keyword in recording_entry.AutomaticEPGRecordingKeywords) { //ConsoleOutputLogger.WriteLine(Keyword +" -> "+currentlyRunningEvent.ShortDescription.Name); if (currentlyRunningEvent.ShortDescription.Name.ToUpper().IndexOf(Keyword.ToUpper()) != -1) { // we found the substring ConsoleOutputLogger.WriteLine("Automatic Recording matched Keyword: " + Keyword); ConsoleOutputLogger.WriteLine("Creating new Recording " + currentlyRunningEvent.ShortDescription.Name + " on Channel " + ChannelAndStationMapper.ServiceID2Name(currentlyRunningEvent.Service)); // only record if either the channel is the channel of the automatic recording or the channel doesn't matter ("") //if ( (recording_entry.Channel != "") || (recording_entry.Channel == ChannelAndStationMapper.Name2Number(ChannelAndStationMapper.ServiceID2Name(currentlyRunningEvent.Service)).ToString())) //{ // we're recording this! currentlyRunningEvent.isRecorded = true; // since we don't know how long this recording will be, we set a maximum time of 4 hours (240 minutes9 Recording newRecording = new Recording(); newRecording.Channel = ChannelAndStationMapper.Name2Number(ChannelAndStationMapper.ServiceID2Name(currentlyRunningEvent.Service)).ToString(); newRecording.createdby = recording_entry.createdby; newRecording.Comment = currentlyRunningEvent.ShortDescription.Text; newRecording.Categories = recording_entry.Categories; newRecording.AdInPosition = recording_entry.AdInPosition; newRecording.AdOutPosition = recording_entry.AdOutPosition; newRecording.EndPosition = recording_entry.EndPosition; newRecording.Episode = recording_entry.Episode; newRecording.EpisodeTitle = recording_entry.EpisodeTitle; newRecording.HoldingTime = recording_entry.HoldingTime; newRecording.isAutomaticEPGRecording = false; newRecording.wasAutomaticEPGRecording = true; newRecording.Recording_Name = currentlyRunningEvent.ShortDescription.Name; newRecording.Username = recording_entry.Username; newRecording.Season = recording_entry.Season; newRecording.Week = recording_entry.Week; newRecording.Year = recording_entry.Year; newRecording.StartsAt = DateTime.Now; newRecording.EndsAt = currentlyRunningEvent.EndTime;//DateTime.Now.AddMinutes(recording_entry.AutomaticRecordingLength); lock (doneRecordings.SyncRoot) { doneRecordings.Add(newRecording.Recording_ID, newRecording); } // fire up the recorder... "true" because we're an recorder and not a streamer VCRandStreaming HReq = new VCRandStreaming(true, newRecording, internal_http_server_object); // tell the console that we're going to record something right now... ConsoleOutputLogger.WriteLine("Record started at " + newRecording.StartsAt.ToShortTimeString() + " - Name: " + newRecording.Recording_Name); Settings.NumberOfRecordings++; // we're recording newRecording.CurrentlyRecording = true; currentlyRunningEvent.AssociatedRecording = newRecording; // call the Handler and lets get back to our job of scheduling... HReq.HandleVCR(ChannelAndStationMapper.Number2Data(Convert.ToInt32(newRecording.Channel)), internal_http_server_object); continue; //} } } #endregion } } } #endregion } else { // TODO: maybe we should also check if the EndsAt is also reached; so we do not start recordings that already passed by if ((recording_entry.StartsAt.Ticks - DateTime.Now.Ticks) <= 0) { // TODO: Recording "done" list handling... lock (doneRecordings.SyncRoot) { // move the recording to the "done" list doneRecordings.Add(recording_entry.Recording_ID, recording_entry); } // remove the recording from the todo-Recordings List Recordings.Remove(recording_entry.Recording_ID); #region Reoccuring Recordings // everything regarding reoccuring event handling takes place here // first check if we have to do anything // TODO: we're currently only checking for "each", not for anything else if (recording_entry.isDaily) { int StartDay = CalcDayOfWeekNumber(DateTime.Now.DayOfWeek); int Counter = 0; bool done2 = false; while (!done2) { StartDay++; Counter++; if (StartDay == 7) StartDay = 0; if (recording_entry.Week[StartDay] == true) done2 = true; } Counter = Counter * recording_entry.isEach; Recording newRecording = recording_entry.Clone(); newRecording.StartsAt = newRecording.StartsAt.AddDays(Convert.ToDouble(Counter)); newRecording.EndsAt = newRecording.EndsAt.AddDays(Convert.ToDouble(Counter)); lock (Recordings.SyncRoot) { Recordings.Add(newRecording.Recording_ID, newRecording); } } #endregion // fire up the recorder... "true" because we're an recorder and not a streamer VCRandStreaming HReq = new VCRandStreaming(true, recording_entry, internal_http_server_object); // tell the console that we're going to record something right now... ConsoleOutputLogger.WriteLine("Record started at " + recording_entry.StartsAt.ToShortTimeString() + " - Name: " + recording_entry.Recording_Name); Settings.NumberOfRecordings++; // we're recording recording_entry.CurrentlyRecording = true; // call the Handler and lets get back to our job of scheduling... HReq.HandleVCR(ChannelAndStationMapper.Number2Data(Convert.ToInt32(recording_entry.Channel)), internal_http_server_object); break; } } } } // wait another 1000 mseconds, then look again for recordings... Thread.Sleep(1000); } catch (Exception e) { // well, most likely we will run into some sync issues with the hashtables...but // since I don't want to deal with this right now I am just going to ignore it.. // does work, you should try that in real life as well. //ConsoleOutputLogger.WriteLine("Scheduler Error: " + e.Message); } } }