From ac69327f1fa866c20630d9d0e0c663472b726c79 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 5 May 2014 00:36:45 -0400 Subject: check in open subtitles stub --- OpenSubtitlesHandler/Console/OSHConsole.cs | 92 + OpenSubtitlesHandler/Interfaces/IMethodResponse.cs | 62 + .../Interfaces/MethodResponseAttr.cs | 40 + .../Languages/DetectLanguageResult.cs | 31 + .../MethodResponses/MethodResponseAddComment.cs | 32 + .../MethodResponses/MethodResponseAddRequest.cs | 35 + .../MethodResponses/MethodResponseAutoUpdate.cs | 60 + .../MethodResponseCheckMovieHash.cs | 37 + .../MethodResponseCheckMovieHash2.cs | 37 + .../MethodResponses/MethodResponseCheckSubHash.cs | 38 + .../MethodResponseDetectLanguage.cs | 37 + .../MethodResponses/MethodResponseError.cs | 36 + .../MethodResponseGetAvailableTranslations.cs | 37 + .../MethodResponses/MethodResponseGetComments.cs | 41 + .../MethodResponseGetSubLanguages.cs | 39 + .../MethodResponseGetTranslation.cs | 37 + .../MethodResponses/MethodResponseInsertMovie.cs | 37 + .../MethodResponseInsertMovieHash.cs | 38 + .../MethodResponses/MethodResponseLogIn.cs | 40 + .../MethodResponses/MethodResponseMovieDetails.cs | 73 + .../MethodResponses/MethodResponseMovieSearch.cs | 43 + .../MethodResponses/MethodResponseNoOperation.cs | 53 + .../MethodResponseReportWrongImdbMovie.cs | 33 + .../MethodResponseReportWrongMovieHash.cs | 34 + .../MethodResponses/MethodResponseSearchToMail.cs | 32 + .../MethodResponses/MethodResponseServerInfo.cs | 132 ++ .../MethodResponseSubtitleDownload.cs | 44 + .../MethodResponseSubtitleSearch.cs | 46 + .../MethodResponses/MethodResponseSubtitlesVote.cs | 44 + .../MethodResponseTryUploadSubtitles.cs | 40 + .../MethodResponseUploadSubtitles.cs | 39 + OpenSubtitlesHandler/Methods Implemeted.txt | 39 + OpenSubtitlesHandler/MovieHasher.cs | 59 + OpenSubtitlesHandler/Movies/CheckMovieHash2Data.cs | 42 + .../Movies/CheckMovieHash2Result.cs | 37 + .../Movies/CheckMovieHashResult.cs | 42 + .../Movies/InsertMovieHashParameters.cs | 38 + OpenSubtitlesHandler/Movies/MovieSearchResult.cs | 41 + .../Movies/SearchToMailMovieParameter.cs | 30 + OpenSubtitlesHandler/OpenSubtitles.cs | 2387 ++++++++++++++++++++ OpenSubtitlesHandler/OpenSubtitlesHandler.csproj | 123 + .../OtherTypes/GetAvailableTranslationsResult.cs | 39 + .../OtherTypes/GetCommentsResult.cs | 37 + OpenSubtitlesHandler/Properties/AssemblyInfo.cs | 36 + OpenSubtitlesHandler/Readme.txt | 20 + .../SubtitleTypes/CheckSubHashResult.cs | 31 + .../SubtitleTypes/SubtitleDownloadResult.cs | 44 + .../SubtitleTypes/SubtitleLanguage.cs | 39 + .../SubtitleTypes/SubtitleSearchParameters.cs | 115 + .../SubtitleTypes/SubtitleSearchResult.cs | 137 ++ .../SubtitleTypes/TryUploadSubtitlesParameters.cs | 48 + .../SubtitleTypes/UploadSubtitleInfoParameter.cs | 46 + .../SubtitleTypes/UploadSubtitleParameters.cs | 53 + OpenSubtitlesHandler/Utilities.cs | 235 ++ OpenSubtitlesHandler/XML-RPC/Docs/XML-RPC.txt | 225 ++ .../XML-RPC/Enums/XmlRpcValueType.cs | 25 + .../XML-RPC/Types/XmlRpcMethodCall.cs | 63 + .../XML-RPC/Values/IXmlRpcValue.cs | 37 + .../XML-RPC/Values/XmlRpcStructMember.cs | 44 + .../XML-RPC/Values/XmlRpcValueArray.cs | 121 + .../XML-RPC/Values/XmlRpcValueBasic.cs | 100 + .../XML-RPC/Values/XmlRpcValueStruct.cs | 43 + OpenSubtitlesHandler/XML-RPC/XmlRpcGenerator.cs | 323 +++ 63 files changed, 6118 insertions(+) create mode 100644 OpenSubtitlesHandler/Console/OSHConsole.cs create mode 100644 OpenSubtitlesHandler/Interfaces/IMethodResponse.cs create mode 100644 OpenSubtitlesHandler/Interfaces/MethodResponseAttr.cs create mode 100644 OpenSubtitlesHandler/Languages/DetectLanguageResult.cs create mode 100644 OpenSubtitlesHandler/MethodResponses/MethodResponseAddComment.cs create mode 100644 OpenSubtitlesHandler/MethodResponses/MethodResponseAddRequest.cs create mode 100644 OpenSubtitlesHandler/MethodResponses/MethodResponseAutoUpdate.cs create mode 100644 OpenSubtitlesHandler/MethodResponses/MethodResponseCheckMovieHash.cs create mode 100644 OpenSubtitlesHandler/MethodResponses/MethodResponseCheckMovieHash2.cs create mode 100644 OpenSubtitlesHandler/MethodResponses/MethodResponseCheckSubHash.cs create mode 100644 OpenSubtitlesHandler/MethodResponses/MethodResponseDetectLanguage.cs create mode 100644 OpenSubtitlesHandler/MethodResponses/MethodResponseError.cs create mode 100644 OpenSubtitlesHandler/MethodResponses/MethodResponseGetAvailableTranslations.cs create mode 100644 OpenSubtitlesHandler/MethodResponses/MethodResponseGetComments.cs create mode 100644 OpenSubtitlesHandler/MethodResponses/MethodResponseGetSubLanguages.cs create mode 100644 OpenSubtitlesHandler/MethodResponses/MethodResponseGetTranslation.cs create mode 100644 OpenSubtitlesHandler/MethodResponses/MethodResponseInsertMovie.cs create mode 100644 OpenSubtitlesHandler/MethodResponses/MethodResponseInsertMovieHash.cs create mode 100644 OpenSubtitlesHandler/MethodResponses/MethodResponseLogIn.cs create mode 100644 OpenSubtitlesHandler/MethodResponses/MethodResponseMovieDetails.cs create mode 100644 OpenSubtitlesHandler/MethodResponses/MethodResponseMovieSearch.cs create mode 100644 OpenSubtitlesHandler/MethodResponses/MethodResponseNoOperation.cs create mode 100644 OpenSubtitlesHandler/MethodResponses/MethodResponseReportWrongImdbMovie.cs create mode 100644 OpenSubtitlesHandler/MethodResponses/MethodResponseReportWrongMovieHash.cs create mode 100644 OpenSubtitlesHandler/MethodResponses/MethodResponseSearchToMail.cs create mode 100644 OpenSubtitlesHandler/MethodResponses/MethodResponseServerInfo.cs create mode 100644 OpenSubtitlesHandler/MethodResponses/MethodResponseSubtitleDownload.cs create mode 100644 OpenSubtitlesHandler/MethodResponses/MethodResponseSubtitleSearch.cs create mode 100644 OpenSubtitlesHandler/MethodResponses/MethodResponseSubtitlesVote.cs create mode 100644 OpenSubtitlesHandler/MethodResponses/MethodResponseTryUploadSubtitles.cs create mode 100644 OpenSubtitlesHandler/MethodResponses/MethodResponseUploadSubtitles.cs create mode 100644 OpenSubtitlesHandler/Methods Implemeted.txt create mode 100644 OpenSubtitlesHandler/MovieHasher.cs create mode 100644 OpenSubtitlesHandler/Movies/CheckMovieHash2Data.cs create mode 100644 OpenSubtitlesHandler/Movies/CheckMovieHash2Result.cs create mode 100644 OpenSubtitlesHandler/Movies/CheckMovieHashResult.cs create mode 100644 OpenSubtitlesHandler/Movies/InsertMovieHashParameters.cs create mode 100644 OpenSubtitlesHandler/Movies/MovieSearchResult.cs create mode 100644 OpenSubtitlesHandler/Movies/SearchToMailMovieParameter.cs create mode 100644 OpenSubtitlesHandler/OpenSubtitles.cs create mode 100644 OpenSubtitlesHandler/OpenSubtitlesHandler.csproj create mode 100644 OpenSubtitlesHandler/OtherTypes/GetAvailableTranslationsResult.cs create mode 100644 OpenSubtitlesHandler/OtherTypes/GetCommentsResult.cs create mode 100644 OpenSubtitlesHandler/Properties/AssemblyInfo.cs create mode 100644 OpenSubtitlesHandler/Readme.txt create mode 100644 OpenSubtitlesHandler/SubtitleTypes/CheckSubHashResult.cs create mode 100644 OpenSubtitlesHandler/SubtitleTypes/SubtitleDownloadResult.cs create mode 100644 OpenSubtitlesHandler/SubtitleTypes/SubtitleLanguage.cs create mode 100644 OpenSubtitlesHandler/SubtitleTypes/SubtitleSearchParameters.cs create mode 100644 OpenSubtitlesHandler/SubtitleTypes/SubtitleSearchResult.cs create mode 100644 OpenSubtitlesHandler/SubtitleTypes/TryUploadSubtitlesParameters.cs create mode 100644 OpenSubtitlesHandler/SubtitleTypes/UploadSubtitleInfoParameter.cs create mode 100644 OpenSubtitlesHandler/SubtitleTypes/UploadSubtitleParameters.cs create mode 100644 OpenSubtitlesHandler/Utilities.cs create mode 100644 OpenSubtitlesHandler/XML-RPC/Docs/XML-RPC.txt create mode 100644 OpenSubtitlesHandler/XML-RPC/Enums/XmlRpcValueType.cs create mode 100644 OpenSubtitlesHandler/XML-RPC/Types/XmlRpcMethodCall.cs create mode 100644 OpenSubtitlesHandler/XML-RPC/Values/IXmlRpcValue.cs create mode 100644 OpenSubtitlesHandler/XML-RPC/Values/XmlRpcStructMember.cs create mode 100644 OpenSubtitlesHandler/XML-RPC/Values/XmlRpcValueArray.cs create mode 100644 OpenSubtitlesHandler/XML-RPC/Values/XmlRpcValueBasic.cs create mode 100644 OpenSubtitlesHandler/XML-RPC/Values/XmlRpcValueStruct.cs create mode 100644 OpenSubtitlesHandler/XML-RPC/XmlRpcGenerator.cs (limited to 'OpenSubtitlesHandler') diff --git a/OpenSubtitlesHandler/Console/OSHConsole.cs b/OpenSubtitlesHandler/Console/OSHConsole.cs new file mode 100644 index 0000000000..04c00bf253 --- /dev/null +++ b/OpenSubtitlesHandler/Console/OSHConsole.cs @@ -0,0 +1,92 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +using System; + +namespace OpenSubtitlesHandler.Console +{ + public class OSHConsole + { + /// + /// Write line to the console and raise the "LineWritten" event + /// + /// + /// The debug line + /// The status + public static void WriteLine(string text, DebugCode code = DebugCode.None) + { + if (LineWritten != null) + LineWritten(null, new DebugEventArgs(text, code)); + } + /// + /// Update the last written line + /// + /// The debug line + /// The status + public static void UpdateLine(string text, DebugCode code = DebugCode.None) + { + if (UpdateLastLine != null) + UpdateLastLine(null, new DebugEventArgs(text, code)); + } + + public static event EventHandler LineWritten; + public static event EventHandler UpdateLastLine; + } + public enum DebugCode + { + None, + Good, + Warning, + Error + } + /// + /// Console Debug Args + /// + public class DebugEventArgs : System.EventArgs + { + public DebugCode Code { get; private set; } + public string Text { get; private set; } + + /// + /// Console Debug Args + /// + /// The debug line + /// The status + public DebugEventArgs(string text, DebugCode code) + { + this.Text = text; + this.Code = code; + } + } + public struct DebugLine + { + public DebugLine(string debugLine, DebugCode status) + { + this.debugLine = debugLine; + this.status = status; + } + string debugLine; + DebugCode status; + + public string Text + { get { return debugLine; } set { debugLine = value; } } + public DebugCode Code + { get { return status; } set { status = value; } } + } +} diff --git a/OpenSubtitlesHandler/Interfaces/IMethodResponse.cs b/OpenSubtitlesHandler/Interfaces/IMethodResponse.cs new file mode 100644 index 0000000000..b8e24f12bd --- /dev/null +++ b/OpenSubtitlesHandler/Interfaces/IMethodResponse.cs @@ -0,0 +1,62 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; +using System.ComponentModel; + +namespace OpenSubtitlesHandler +{ + /// + /// When you call a method to communicate with OpenSubtitles server, that method should return this response with the reuired information. + /// + public abstract class IMethodResponse + { + public IMethodResponse() { LoadAttributes(); } + public IMethodResponse(string name, string message) + { + this.name = name; + this.message = message; + } + protected string name; + protected string message; + protected double seconds; + protected string status; + + protected virtual void LoadAttributes() + { + foreach (Attribute attr in Attribute.GetCustomAttributes(this.GetType())) + { + if (attr.GetType() == typeof(MethodResponseDescription)) + { + this.name = ((MethodResponseDescription)attr).Name; + this.message = ((MethodResponseDescription)attr).Message; + break; + } + } + } + + [Description("The name of this response"), Category("MethodResponse")] + public virtual string Name { get { return name; } set { name = value; } } + [Description("The message about this response"), Category("MethodResponse")] + public virtual string Message { get { return message; } set { message = value; } } + [Description("Time taken to execute this command on server"), Category("MethodResponse")] + public double Seconds { get { return seconds; } set { seconds = value; } } + [Description("The status"), Category("MethodResponse")] + public string Status { get { return status; } set { status = value; } } + } +} diff --git a/OpenSubtitlesHandler/Interfaces/MethodResponseAttr.cs b/OpenSubtitlesHandler/Interfaces/MethodResponseAttr.cs new file mode 100644 index 0000000000..57f01d4d9b --- /dev/null +++ b/OpenSubtitlesHandler/Interfaces/MethodResponseAttr.cs @@ -0,0 +1,40 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; + +namespace OpenSubtitlesHandler +{ + /// + /// Attributes to describe a method response object. + /// + public class MethodResponseDescription : Attribute + { + public MethodResponseDescription(string name, string message) + { + this.name = name; + this.message = message; + } + + private string name; + private string message; + + public string Name { get { return name; } set { name = value; } } + public string Message { get { return message; } set { message = value; } } + } +} diff --git a/OpenSubtitlesHandler/Languages/DetectLanguageResult.cs b/OpenSubtitlesHandler/Languages/DetectLanguageResult.cs new file mode 100644 index 0000000000..9cc3cb8d83 --- /dev/null +++ b/OpenSubtitlesHandler/Languages/DetectLanguageResult.cs @@ -0,0 +1,31 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +namespace OpenSubtitlesHandler +{ + public struct DetectLanguageResult + { + private string inputSample; + private string languageID; + + public string LanguageID + { get { return languageID; } set { languageID = value; } } + public string InputSample + { get { return inputSample; } set { inputSample = value; } } + } +} diff --git a/OpenSubtitlesHandler/MethodResponses/MethodResponseAddComment.cs b/OpenSubtitlesHandler/MethodResponses/MethodResponseAddComment.cs new file mode 100644 index 0000000000..4cfa11cc9c --- /dev/null +++ b/OpenSubtitlesHandler/MethodResponses/MethodResponseAddComment.cs @@ -0,0 +1,32 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +namespace OpenSubtitlesHandler +{ + [MethodResponseDescription("AddComment method response", + "AddComment method response hold all expected values from server.")] + public class MethodResponseAddComment : IMethodResponse + { + public MethodResponseAddComment() + : base() + { } + public MethodResponseAddComment(string name, string message) + : base(name, message) + { } + } +} diff --git a/OpenSubtitlesHandler/MethodResponses/MethodResponseAddRequest.cs b/OpenSubtitlesHandler/MethodResponses/MethodResponseAddRequest.cs new file mode 100644 index 0000000000..8ea1c387c8 --- /dev/null +++ b/OpenSubtitlesHandler/MethodResponses/MethodResponseAddRequest.cs @@ -0,0 +1,35 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +namespace OpenSubtitlesHandler +{ + [MethodResponseDescription("AddRequest method response", + "AddRequest method response hold all expected values from server.")] + public class MethodResponseAddRequest : IMethodResponse + { + public MethodResponseAddRequest() + : base() + { } + public MethodResponseAddRequest(string name, string message) + : base(name, message) + { } + private string _request_url; + + public string request_url { get { return _request_url; } set { _request_url = value; } } + } +} diff --git a/OpenSubtitlesHandler/MethodResponses/MethodResponseAutoUpdate.cs b/OpenSubtitlesHandler/MethodResponses/MethodResponseAutoUpdate.cs new file mode 100644 index 0000000000..26edf8b0ea --- /dev/null +++ b/OpenSubtitlesHandler/MethodResponses/MethodResponseAutoUpdate.cs @@ -0,0 +1,60 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; +using System.ComponentModel; + +namespace OpenSubtitlesHandler +{ + [MethodResponseDescription("AutoUpdate method response", + "AutoUpdate method response hold all expected values from server.")] + public class MethodResponseAutoUpdate : IMethodResponse + { + public MethodResponseAutoUpdate() + : base() + { } + public MethodResponseAutoUpdate(string name, string message) + : base(name, message) + { } + + private string _version; + private string _url_windows; + private string _comments; + private string _url_linux; + /// + /// Latest application version + /// + [Description("Latest application version"), Category("AutoUpdate")] + public string version { get { return _version; } set { _version = value; } } + /// + /// Download URL for Windows version + /// + [Description("Download URL for Windows version"), Category("AutoUpdate")] + public string url_windows { get { return _url_windows; } set { _url_windows = value; } } + /// + /// Application changelog and other comments + /// + [Description("Application changelog and other comments"), Category("AutoUpdate")] + public string comments { get { return _comments; } set { _comments = value; } } + /// + /// Download URL for Linux version + /// + [Description("Download URL for Linux version"), Category("AutoUpdate")] + public string url_linux { get { return _url_linux; } set { _url_linux = value; } } + } +} diff --git a/OpenSubtitlesHandler/MethodResponses/MethodResponseCheckMovieHash.cs b/OpenSubtitlesHandler/MethodResponses/MethodResponseCheckMovieHash.cs new file mode 100644 index 0000000000..30ef075b97 --- /dev/null +++ b/OpenSubtitlesHandler/MethodResponses/MethodResponseCheckMovieHash.cs @@ -0,0 +1,37 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; +using System.Collections.Generic; +namespace OpenSubtitlesHandler +{ + [MethodResponseDescription("CheckMovieHash method response", + "CheckMovieHash method response hold all expected values from server.")] + public class MethodResponseCheckMovieHash : IMethodResponse + { + public MethodResponseCheckMovieHash() + : base() + { } + public MethodResponseCheckMovieHash(string name, string message) + : base(name, message) + { } + private List results = new List(); + public List Results + { get { return results; } set { results = value; } } + } +} diff --git a/OpenSubtitlesHandler/MethodResponses/MethodResponseCheckMovieHash2.cs b/OpenSubtitlesHandler/MethodResponses/MethodResponseCheckMovieHash2.cs new file mode 100644 index 0000000000..78cdef0c4b --- /dev/null +++ b/OpenSubtitlesHandler/MethodResponses/MethodResponseCheckMovieHash2.cs @@ -0,0 +1,37 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; +using System.Collections.Generic; +namespace OpenSubtitlesHandler +{ + [MethodResponseDescription("CheckMovieHash2 method response", + "CheckMovieHash2 method response hold all expected values from server.")] + public class MethodResponseCheckMovieHash2 : IMethodResponse + { + public MethodResponseCheckMovieHash2() + : base() + { } + public MethodResponseCheckMovieHash2(string name, string message) + : base(name, message) + { } + private List results = new List(); + public List Results + { get { return results; } set { results = value; } } + } +} diff --git a/OpenSubtitlesHandler/MethodResponses/MethodResponseCheckSubHash.cs b/OpenSubtitlesHandler/MethodResponses/MethodResponseCheckSubHash.cs new file mode 100644 index 0000000000..45c73631cf --- /dev/null +++ b/OpenSubtitlesHandler/MethodResponses/MethodResponseCheckSubHash.cs @@ -0,0 +1,38 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; +using System.Collections.Generic; + +namespace OpenSubtitlesHandler +{ + [MethodResponseDescription("CheckSubHash method response", + "CheckSubHash method response hold all expected values from server.")] + public class MethodResponseCheckSubHash : IMethodResponse + { + public MethodResponseCheckSubHash() + : base() + { } + public MethodResponseCheckSubHash(string name, string message) + : base(name, message) + { } + private List results = new List(); + + public List Results { get { return results; } set { results = value; } } + } +} diff --git a/OpenSubtitlesHandler/MethodResponses/MethodResponseDetectLanguage.cs b/OpenSubtitlesHandler/MethodResponses/MethodResponseDetectLanguage.cs new file mode 100644 index 0000000000..20b4d30f3e --- /dev/null +++ b/OpenSubtitlesHandler/MethodResponses/MethodResponseDetectLanguage.cs @@ -0,0 +1,37 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System.Collections.Generic; +namespace OpenSubtitlesHandler +{ + [MethodResponseDescription("DetectLanguage method response", + "DetectLanguage method response hold all expected values from server.")] + public class MethodResponseDetectLanguage : IMethodResponse + { + public MethodResponseDetectLanguage() + : base() + { } + public MethodResponseDetectLanguage(string name, string message) + : base(name, message) + { } + private List results = new List(); + + public List Results + { get { return results; } set { results = value; } } + } +} diff --git a/OpenSubtitlesHandler/MethodResponses/MethodResponseError.cs b/OpenSubtitlesHandler/MethodResponses/MethodResponseError.cs new file mode 100644 index 0000000000..273dfb1ad5 --- /dev/null +++ b/OpenSubtitlesHandler/MethodResponses/MethodResponseError.cs @@ -0,0 +1,36 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; +namespace OpenSubtitlesHandler +{ + /// + /// Response that can be used for general error like internet connection fail. + /// + [MethodResponseDescription("Error method response", + "Error method response that describes error that occured")] + public class MethodResponseError : IMethodResponse + { + public MethodResponseError() + : base() + { } + public MethodResponseError(string name, string message) + : base(name, message) + { } + } +} diff --git a/OpenSubtitlesHandler/MethodResponses/MethodResponseGetAvailableTranslations.cs b/OpenSubtitlesHandler/MethodResponses/MethodResponseGetAvailableTranslations.cs new file mode 100644 index 0000000000..1ee4ea6c06 --- /dev/null +++ b/OpenSubtitlesHandler/MethodResponses/MethodResponseGetAvailableTranslations.cs @@ -0,0 +1,37 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; +using System.Collections.Generic; + +namespace OpenSubtitlesHandler +{ + [MethodResponseDescription("GetAvailableTranslations method response", + "GetAvailableTranslations method response hold all expected values from server.")] + public class MethodResponseGetAvailableTranslations : IMethodResponse + { + public MethodResponseGetAvailableTranslations() + : base() + { } + public MethodResponseGetAvailableTranslations(string name, string message) + : base(name, message) + { } + private List results = new List(); + public List Results { get { return results; } set { results = value; } } + } +} diff --git a/OpenSubtitlesHandler/MethodResponses/MethodResponseGetComments.cs b/OpenSubtitlesHandler/MethodResponses/MethodResponseGetComments.cs new file mode 100644 index 0000000000..6a586d5ced --- /dev/null +++ b/OpenSubtitlesHandler/MethodResponses/MethodResponseGetComments.cs @@ -0,0 +1,41 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace OpenSubtitlesHandler +{ + [MethodResponseDescription("GetComments method response", + "GetComments method response hold all expected values from server.")] + public class MethodResponseGetComments : IMethodResponse + { + public MethodResponseGetComments() + : base() + { } + public MethodResponseGetComments(string name, string message) + : base(name, message) + { } + private List results = new List(); + + public List Results + { get { return results; } set { results = value; } } + } +} diff --git a/OpenSubtitlesHandler/MethodResponses/MethodResponseGetSubLanguages.cs b/OpenSubtitlesHandler/MethodResponses/MethodResponseGetSubLanguages.cs new file mode 100644 index 0000000000..dc8100c92e --- /dev/null +++ b/OpenSubtitlesHandler/MethodResponses/MethodResponseGetSubLanguages.cs @@ -0,0 +1,39 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; +using System.Collections.Generic; + +namespace OpenSubtitlesHandler +{ + [MethodResponseDescription("GetSubLanguages method response", + "GetSubLanguages method response hold all expected values from server.")] + public class MethodResponseGetSubLanguages : IMethodResponse + { + public MethodResponseGetSubLanguages() + : base() + { } + public MethodResponseGetSubLanguages(string name, string message) + : base(name, message) + { } + private List languages = new List(); + + public List Languages + { get { return languages; } set { languages = value; } } + } +} diff --git a/OpenSubtitlesHandler/MethodResponses/MethodResponseGetTranslation.cs b/OpenSubtitlesHandler/MethodResponses/MethodResponseGetTranslation.cs new file mode 100644 index 0000000000..5482468873 --- /dev/null +++ b/OpenSubtitlesHandler/MethodResponses/MethodResponseGetTranslation.cs @@ -0,0 +1,37 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; + +namespace OpenSubtitlesHandler +{ + [MethodResponseDescription("GetTranslation method response", + "GetTranslation method response hold all expected values from server.")] + public class MethodResponseGetTranslation : IMethodResponse + { + public MethodResponseGetTranslation() + : base() + { } + public MethodResponseGetTranslation(string name, string message) + : base(name, message) + { } + private string data; + + public string ContentData { get { return data; } set { data = value; } } + } +} diff --git a/OpenSubtitlesHandler/MethodResponses/MethodResponseInsertMovie.cs b/OpenSubtitlesHandler/MethodResponses/MethodResponseInsertMovie.cs new file mode 100644 index 0000000000..b53aad5a9d --- /dev/null +++ b/OpenSubtitlesHandler/MethodResponses/MethodResponseInsertMovie.cs @@ -0,0 +1,37 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; + +namespace OpenSubtitlesHandler +{ + [MethodResponseDescription("InsertMovie method response", + "InsertMovie method response hold all expected values from server.")] + public class MethodResponseInsertMovie : IMethodResponse + { + public MethodResponseInsertMovie() + : base() + { } + public MethodResponseInsertMovie(string name, string message) + : base(name, message) + { } + private string _ID; + public string ID + { get { return _ID; } set { _ID = value; } } + } +} diff --git a/OpenSubtitlesHandler/MethodResponses/MethodResponseInsertMovieHash.cs b/OpenSubtitlesHandler/MethodResponses/MethodResponseInsertMovieHash.cs new file mode 100644 index 0000000000..fe9196de87 --- /dev/null +++ b/OpenSubtitlesHandler/MethodResponses/MethodResponseInsertMovieHash.cs @@ -0,0 +1,38 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System.Collections.Generic; +namespace OpenSubtitlesHandler +{ + [MethodResponseDescription("InsertMovieHash method response", + "InsertMovieHash method response hold all expected values from server.")] + public class MethodResponseInsertMovieHash : IMethodResponse + { + public MethodResponseInsertMovieHash() + : base() + { } + public MethodResponseInsertMovieHash(string name, string message) + : base(name, message) + { } + private List _accepted_moviehashes = new List(); + private List _new_imdbs = new List(); + + public List accepted_moviehashes { get { return _accepted_moviehashes; } set { _accepted_moviehashes = value; } } + public List new_imdbs { get { return _new_imdbs; } set { _new_imdbs = value; } } + } +} diff --git a/OpenSubtitlesHandler/MethodResponses/MethodResponseLogIn.cs b/OpenSubtitlesHandler/MethodResponses/MethodResponseLogIn.cs new file mode 100644 index 0000000000..e7c23f61ca --- /dev/null +++ b/OpenSubtitlesHandler/MethodResponses/MethodResponseLogIn.cs @@ -0,0 +1,40 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; + +namespace OpenSubtitlesHandler +{ + /// + /// Response can be used for log in/out operation. + /// + [MethodResponseDescription("LogIn method response", + "LogIn method response hold all expected values from server.")] + public class MethodResponseLogIn : IMethodResponse + { + public MethodResponseLogIn() + : base() + { } + public MethodResponseLogIn(string name, string message) + : base(name, message) + { } + private string token; + + public string Token { get { return token; } set { token = value; } } + } +} diff --git a/OpenSubtitlesHandler/MethodResponses/MethodResponseMovieDetails.cs b/OpenSubtitlesHandler/MethodResponses/MethodResponseMovieDetails.cs new file mode 100644 index 0000000000..29e19245c1 --- /dev/null +++ b/OpenSubtitlesHandler/MethodResponses/MethodResponseMovieDetails.cs @@ -0,0 +1,73 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; +using System.Collections.Generic; + +namespace OpenSubtitlesHandler +{ + [MethodResponseDescription("MovieDetails method response", + "MovieDetails method response hold all expected values from server.")] + public class MethodResponseMovieDetails : IMethodResponse + { + public MethodResponseMovieDetails() + : base() + { } + public MethodResponseMovieDetails(string name, string message) + : base(name, message) + { } + // Details + private string id; + private string title; + private string year; + private string coverLink; + + private string duration; + private string tagline; + private string plot; + private string goofs; + private string trivia; + private List cast = new List(); + private List directors = new List(); + private List writers = new List(); + private List awards = new List(); + private List genres = new List(); + private List country = new List(); + private List language = new List(); + private List certification = new List(); + + // Details + public string ID { get { return id; } set { id = value; } } + public string Title { get { return title; } set { title = value; } } + public string Year { get { return year; } set { year = value; } } + public string CoverLink { get { return coverLink; } set { coverLink = value; } } + public string Duration { get { return duration; } set { duration = value; } } + public string Tagline { get { return tagline; } set { tagline = value; } } + public string Plot { get { return plot; } set { plot = value; } } + public string Goofs { get { return goofs; } set { goofs = value; } } + public string Trivia { get { return trivia; } set { trivia = value; } } + public List Cast { get { return cast; } set { cast = value; } } + public List Directors { get { return directors; } set { directors = value; } } + public List Writers { get { return writers; } set { writers = value; } } + public List Genres { get { return genres; } set { genres = value; } } + public List Awards { get { return awards; } set { awards = value; } } + public List Country { get { return country; } set { country = value; } } + public List Language { get { return language; } set { language = value; } } + public List Certification { get { return certification; } set { certification = value; } } + } +} diff --git a/OpenSubtitlesHandler/MethodResponses/MethodResponseMovieSearch.cs b/OpenSubtitlesHandler/MethodResponses/MethodResponseMovieSearch.cs new file mode 100644 index 0000000000..c1beeeeeae --- /dev/null +++ b/OpenSubtitlesHandler/MethodResponses/MethodResponseMovieSearch.cs @@ -0,0 +1,43 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; +using System.Collections.Generic; + +namespace OpenSubtitlesHandler +{ + [MethodResponseDescription("MovieSearch method response", + "MovieSearch method response hold all expected values from server.")] + public class MethodResponseMovieSearch : IMethodResponse + { + public MethodResponseMovieSearch() + : base() + { + results = new List(); + } + public MethodResponseMovieSearch(string name, string message) + : base(name, message) + { + results = new List(); + } + private List results; + + public List Results + { get { return results; } set { results = value; } } + } +} diff --git a/OpenSubtitlesHandler/MethodResponses/MethodResponseNoOperation.cs b/OpenSubtitlesHandler/MethodResponses/MethodResponseNoOperation.cs new file mode 100644 index 0000000000..0b5b2f1aab --- /dev/null +++ b/OpenSubtitlesHandler/MethodResponses/MethodResponseNoOperation.cs @@ -0,0 +1,53 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; + +namespace OpenSubtitlesHandler +{ + [MethodResponseDescription("NoOperation method response", + "NoOperation method response hold all expected values from server.")] + public class MethodResponseNoOperation : IMethodResponse + { + public MethodResponseNoOperation() + : base() { } + public MethodResponseNoOperation(string name, string message) + : base(name, message) + { } + + private string _global_wrh_download_limit; + private string _client_ip; + private string _limit_check_by; + private string _client_24h_download_count; + private string _client_downlaod_quota; + private string _client_24h_download_limit; + + public string global_wrh_download_limit + { get { return _global_wrh_download_limit; } set { _global_wrh_download_limit = value; } } + public string client_ip + { get { return _client_ip; } set { _client_ip = value; } } + public string limit_check_by + { get { return _limit_check_by; } set { _limit_check_by = value; } } + public string client_24h_download_count + { get { return _client_24h_download_count; } set { _client_24h_download_count = value; } } + public string client_downlaod_quota + { get { return _client_downlaod_quota; } set { _client_downlaod_quota = value; } } + public string client_24h_download_limit + { get { return _client_24h_download_limit; } set { _client_24h_download_limit = value; } } + } +} diff --git a/OpenSubtitlesHandler/MethodResponses/MethodResponseReportWrongImdbMovie.cs b/OpenSubtitlesHandler/MethodResponses/MethodResponseReportWrongImdbMovie.cs new file mode 100644 index 0000000000..3c19fcf6e5 --- /dev/null +++ b/OpenSubtitlesHandler/MethodResponses/MethodResponseReportWrongImdbMovie.cs @@ -0,0 +1,33 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +namespace OpenSubtitlesHandler +{ + [MethodResponseDescription("ReportWrongImdbMovie method response", + "ReportWrongImdbMovie method response hold all expected values from server.")] + public class MethodResponseReportWrongImdbMovie : IMethodResponse + { + public MethodResponseReportWrongImdbMovie() + : base() + { } + public MethodResponseReportWrongImdbMovie(string name, string message) + : base(name, message) + { } + } +} diff --git a/OpenSubtitlesHandler/MethodResponses/MethodResponseReportWrongMovieHash.cs b/OpenSubtitlesHandler/MethodResponses/MethodResponseReportWrongMovieHash.cs new file mode 100644 index 0000000000..94788b2c68 --- /dev/null +++ b/OpenSubtitlesHandler/MethodResponses/MethodResponseReportWrongMovieHash.cs @@ -0,0 +1,34 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; + +namespace OpenSubtitlesHandler +{ + [MethodResponseDescription("ReportWrongMovieHash method response", + "ReportWrongMovieHash method response hold all expected values from server.")] + public class MethodResponseReportWrongMovieHash : IMethodResponse + { + public MethodResponseReportWrongMovieHash() + : base() + { } + public MethodResponseReportWrongMovieHash(string name, string message) + : base(name, message) + { } + } +} diff --git a/OpenSubtitlesHandler/MethodResponses/MethodResponseSearchToMail.cs b/OpenSubtitlesHandler/MethodResponses/MethodResponseSearchToMail.cs new file mode 100644 index 0000000000..9f195dea05 --- /dev/null +++ b/OpenSubtitlesHandler/MethodResponses/MethodResponseSearchToMail.cs @@ -0,0 +1,32 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +namespace OpenSubtitlesHandler +{ + [MethodResponseDescription("SearchToMail method response", + "SearchToMail method response hold all expected values from server.")] + public class MethodResponseSearchToMail : IMethodResponse + { + public MethodResponseSearchToMail() + : base() + { } + public MethodResponseSearchToMail(string name, string message) + : base(name, message) + { } + } +} diff --git a/OpenSubtitlesHandler/MethodResponses/MethodResponseServerInfo.cs b/OpenSubtitlesHandler/MethodResponses/MethodResponseServerInfo.cs new file mode 100644 index 0000000000..fce5b42450 --- /dev/null +++ b/OpenSubtitlesHandler/MethodResponses/MethodResponseServerInfo.cs @@ -0,0 +1,132 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; +using System.Collections.Generic; +using System.ComponentModel; +namespace OpenSubtitlesHandler +{ + [MethodResponseDescription("ServerInfo method response", + "ServerInfo method response hold all expected values from server.")] + public class MethodResponseServerInfo : IMethodResponse + { + public MethodResponseServerInfo() + : base() + { } + public MethodResponseServerInfo(string name, string message) + : base(name, message) + { } + private string _xmlrpc_version; + private string _xmlrpc_url; + private string _application; + private string _contact; + private string _website_url; + private int _users_online_total; + private int _users_online_program; + private int _users_loggedin; + private string _users_max_alltime; + private string _users_registered; + private string _subs_downloads; + private string _subs_subtitle_files; + private string _movies_total; + private string _movies_aka; + private string _total_subtitles_languages; + private List _last_update_strings = new List(); + + /// + /// Version of server's XML-RPC API implementation + /// + [Description("Version of server's XML-RPC API implementation"), Category("OS")] + public string xmlrpc_version { get { return _xmlrpc_version; } set { _xmlrpc_version = value; } } + /// + /// XML-RPC interface URL + /// + [Description("XML-RPC interface URL"), Category("OS")] + public string xmlrpc_url { get { return _xmlrpc_url; } set { _xmlrpc_url = value; } } + /// + /// Server's application name and version + /// + [Description("Server's application name and version"), Category("OS")] + public string application { get { return _application; } set { _application = value; } } + /// + /// Contact e-mail address for server related quuestions and problems + /// + [Description("Contact e-mail address for server related quuestions and problems"), Category("OS")] + public string contact { get { return _contact; } set { _contact = value; } } + /// + /// Main server URL + /// + [Description("Main server URL"), Category("OS")] + public string website_url { get { return _website_url; } set { _website_url = value; } } + /// + /// Number of users currently online + /// + [Description("Number of users currently online"), Category("OS")] + public int users_online_total { get { return _users_online_total; } set { _users_online_total = value; } } + /// + /// Number of users currently online using a client application (XML-RPC API) + /// + [Description("Number of users currently online using a client application (XML-RPC API)"), Category("OS")] + public int users_online_program { get { return _users_online_program; } set { _users_online_program = value; } } + /// + /// Number of currently logged-in users + /// + [Description("Number of currently logged-in users"), Category("OS")] + public int users_loggedin { get { return _users_loggedin; } set { _users_loggedin = value; } } + /// + /// Maximum number of users throughout the history + /// + [Description("Maximum number of users throughout the history"), Category("OS")] + public string users_max_alltime { get { return _users_max_alltime; } set { _users_max_alltime = value; } } + /// + /// Number of registered users + /// + [Description("Number of registered users"), Category("OS")] + public string users_registered { get { return _users_registered; } set { _users_registered = value; } } + /// + /// Total number of subtitle downloads + /// + [Description("Total number of subtitle downloads"), Category("OS")] + public string subs_downloads { get { return _subs_downloads; } set { _subs_downloads = value; } } + /// + /// Total number of subtitle files stored on the server + /// + [Description("Total number of subtitle files stored on the server"), Category("OS")] + public string subs_subtitle_files { get { return _subs_subtitle_files; } set { _subs_subtitle_files = value; } } + /// + /// Total number of movies in the database + /// + [Description("Total number of movies in the database"), Category("OS")] + public string movies_total { get { return _movies_total; } set { _movies_total = value; } } + /// + /// Total number of movie A.K.A. titles in the database + /// + [Description("Total number of movie A.K.A. titles in the database"), Category("OS")] + public string movies_aka { get { return _movies_aka; } set { _movies_aka = value; } } + /// + /// Total number of subtitle languages supported + /// + [Description("Total number of subtitle languages supported"), Category("OS")] + public string total_subtitles_languages { get { return _total_subtitles_languages; } set { _total_subtitles_languages = value; } } + /// + /// Structure containing information about last updates of translations. + /// + [Description("Structure containing information about last updates of translations"), Category("OS")] + public List last_update_strings { get { return _last_update_strings; } set { _last_update_strings = value; } } + } +} diff --git a/OpenSubtitlesHandler/MethodResponses/MethodResponseSubtitleDownload.cs b/OpenSubtitlesHandler/MethodResponses/MethodResponseSubtitleDownload.cs new file mode 100644 index 0000000000..7ad9f38ec4 --- /dev/null +++ b/OpenSubtitlesHandler/MethodResponses/MethodResponseSubtitleDownload.cs @@ -0,0 +1,44 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; +using System.Collections.Generic; +namespace OpenSubtitlesHandler +{ + /// + /// The response that should be used by subtitle download methods. + /// + [MethodResponseDescription("SubtitleDownload method response", + "SubtitleDownload method response hold all expected values from server.")] + public class MethodResponseSubtitleDownload : IMethodResponse + { + public MethodResponseSubtitleDownload() + : base() + { + results = new List(); + } + public MethodResponseSubtitleDownload(string name, string message) + : base(name, message) + { + results = new List(); + } + private List results; + public List Results + { get { return results; } set { results = value; } } + } +} diff --git a/OpenSubtitlesHandler/MethodResponses/MethodResponseSubtitleSearch.cs b/OpenSubtitlesHandler/MethodResponses/MethodResponseSubtitleSearch.cs new file mode 100644 index 0000000000..c359c0ffd0 --- /dev/null +++ b/OpenSubtitlesHandler/MethodResponses/MethodResponseSubtitleSearch.cs @@ -0,0 +1,46 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; +using System.Collections.Generic; + +namespace OpenSubtitlesHandler +{ + /// + /// Response to SearchSubtitle successed call. + /// + [MethodResponseDescription("SubtitleSearch method response", + "SubtitleSearch method response hold all expected values from server.")] + public class MethodResponseSubtitleSearch : IMethodResponse + { + public MethodResponseSubtitleSearch() + : base() + { + results = new List(); + } + public MethodResponseSubtitleSearch(string name, string message) + : base(name, message) + { + results = new List(); + } + + private List results; + public List Results + { get { return results; } set { results = value; } } + } +} diff --git a/OpenSubtitlesHandler/MethodResponses/MethodResponseSubtitlesVote.cs b/OpenSubtitlesHandler/MethodResponses/MethodResponseSubtitlesVote.cs new file mode 100644 index 0000000000..1f5364f0c1 --- /dev/null +++ b/OpenSubtitlesHandler/MethodResponses/MethodResponseSubtitlesVote.cs @@ -0,0 +1,44 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; + +namespace OpenSubtitlesHandler +{ + [MethodResponseDescription("SubtitlesVote method response", + "SubtitlesVote method response hold all expected values from server.")] + public class MethodResponseSubtitlesVote : IMethodResponse + { + public MethodResponseSubtitlesVote() + : base() + { } + public MethodResponseSubtitlesVote(string name, string message) + : base(name, message) + { } + private string _SubRating; + private string _SubSumVotes; + private string _IDSubtitle; + + public string SubRating + { get { return _SubRating; } set { _SubRating = value; } } + public string SubSumVotes + { get { return _SubSumVotes; } set { _SubSumVotes = value; } } + public string IDSubtitle + { get { return _IDSubtitle; } set { _IDSubtitle = value; } } + } +} diff --git a/OpenSubtitlesHandler/MethodResponses/MethodResponseTryUploadSubtitles.cs b/OpenSubtitlesHandler/MethodResponses/MethodResponseTryUploadSubtitles.cs new file mode 100644 index 0000000000..9dbf1576db --- /dev/null +++ b/OpenSubtitlesHandler/MethodResponses/MethodResponseTryUploadSubtitles.cs @@ -0,0 +1,40 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; +using System.Collections.Generic; + +namespace OpenSubtitlesHandler +{ + [MethodResponseDescription("TryUploadSubtitles method response", + "TryUploadSubtitles method response hold all expected values from server.")] + public class MethodResponseTryUploadSubtitles : IMethodResponse + { + public MethodResponseTryUploadSubtitles() + : base() + { } + public MethodResponseTryUploadSubtitles(string name, string message) + : base(name, message) + { } + private int alreadyindb; + private List results = new List(); + + public int AlreadyInDB { get { return alreadyindb; } set { alreadyindb = value; } } + public List Results { get { return results; } set { results = value; } } + } +} diff --git a/OpenSubtitlesHandler/MethodResponses/MethodResponseUploadSubtitles.cs b/OpenSubtitlesHandler/MethodResponses/MethodResponseUploadSubtitles.cs new file mode 100644 index 0000000000..3b2320db28 --- /dev/null +++ b/OpenSubtitlesHandler/MethodResponses/MethodResponseUploadSubtitles.cs @@ -0,0 +1,39 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; + +namespace OpenSubtitlesHandler +{ + [MethodResponseDescription("UploadSubtitles method response", + "UploadSubtitles method response hold all expected values from server.")] + public class MethodResponseUploadSubtitles : IMethodResponse + { + public MethodResponseUploadSubtitles() + : base() + { } + public MethodResponseUploadSubtitles(string name, string message) + : base(name, message) + { } + private string _data; + private bool _subtitles; + + public string Data { get { return _data; } set { _data = value; } } + public bool SubTitles { get { return _subtitles; } set { _subtitles = value; } } + } +} diff --git a/OpenSubtitlesHandler/Methods Implemeted.txt b/OpenSubtitlesHandler/Methods Implemeted.txt new file mode 100644 index 0000000000..5d7ae0d493 --- /dev/null +++ b/OpenSubtitlesHandler/Methods Implemeted.txt @@ -0,0 +1,39 @@ +List of available OpenSubtitles.org server XML-RPC methods. +========================================================== +Legends: +* OK: this method is fully implemented, tested and works fine. +* TODO: this method is in the plan to be added. +* NOT TESTED: this method added and expected to work fine but never tested. +* NOT WORK (x): this method added but not work. x= Description of the error. + +-------------------------------------------- +Method name | Status +-------------------------|------------------ +LogIn | OK +LogOut | OK +NoOperation | OK +SearchSubtitles | OK +DownloadSubtitles | OK +SearchToMail | OK +TryUploadSubtitles | OK +UploadSubtitles | OK +SearchMoviesOnIMDB | OK +GetIMDBMovieDetails | OK +InsertMovie | OK +InsertMovieHash | OK +ServerInfo | OK +ReportWrongMovieHash | OK +ReportWrongImdbMovie | OK +SubtitlesVote | OK +AddComment | OK +AddRequest | OK +GetComments | OK +GetSubLanguages | OK +DetectLanguage | OK +GetAvailableTranslations | OK +GetTranslation | NOT WORK (Returns status of error 410 'Other or unknown error') +AutoUpdate | NOT WORK (Returns status: 'parse error. not well formed') +CheckMovieHash | OK +CheckMovieHash2 | OK +CheckSubHash | OK +-------------------------------------------- \ No newline at end of file diff --git a/OpenSubtitlesHandler/MovieHasher.cs b/OpenSubtitlesHandler/MovieHasher.cs new file mode 100644 index 0000000000..a14bd20f8b --- /dev/null +++ b/OpenSubtitlesHandler/MovieHasher.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace OpenSubtitlesHandler +{ + public class MovieHasher + { + public static byte[] ComputeMovieHash(string filename) + { + byte[] result; + using (Stream input = File.OpenRead(filename)) + { + result = ComputeMovieHash(input); + } + return result; + } + + private static byte[] ComputeMovieHash(Stream input) + { + long lhash, streamsize; + streamsize = input.Length; + lhash = streamsize; + + long i = 0; + byte[] buffer = new byte[sizeof(long)]; + while (i < 65536 / sizeof(long) && (input.Read(buffer, 0, sizeof(long)) > 0)) + { + i++; + lhash += BitConverter.ToInt64(buffer, 0); + } + + input.Position = Math.Max(0, streamsize - 65536); + i = 0; + while (i < 65536 / sizeof(long) && (input.Read(buffer, 0, sizeof(long)) > 0)) + { + i++; + lhash += BitConverter.ToInt64(buffer, 0); + } + input.Close(); + byte[] result = BitConverter.GetBytes(lhash); + Array.Reverse(result); + return result; + } + + public static string ToHexadecimal(byte[] bytes) + { + StringBuilder hexBuilder = new StringBuilder(); + for (int i = 0; i < bytes.Length; i++) + { + hexBuilder.Append(bytes[i].ToString("x2")); + } + return hexBuilder.ToString(); + } + } +} diff --git a/OpenSubtitlesHandler/Movies/CheckMovieHash2Data.cs b/OpenSubtitlesHandler/Movies/CheckMovieHash2Data.cs new file mode 100644 index 0000000000..4f6de82b1a --- /dev/null +++ b/OpenSubtitlesHandler/Movies/CheckMovieHash2Data.cs @@ -0,0 +1,42 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +namespace OpenSubtitlesHandler +{ + public struct CheckMovieHash2Data + { + private string _MovieHash; + private string _MovieImdbID; + private string _MovieName; + private string _MovieYear; + private string _MovieKind; + private string _SeriesSeason; + private string _SeriesEpisode; + private string _SeenCount; + + public string MovieHash { get { return _MovieHash; } set { _MovieHash = value; } } + public string MovieImdbID { get { return _MovieImdbID; } set { _MovieImdbID = value; } } + public string MovieName { get { return _MovieName; } set { _MovieName = value; } } + public string MovieYear { get { return _MovieYear; } set { _MovieYear = value; } } + public string MovieKind { get { return _MovieKind; } set { _MovieKind = value; } } + public string SeriesSeason { get { return _SeriesSeason; } set { _SeriesSeason = value; } } + public string SeriesEpisode { get { return _SeriesEpisode; } set { _SeriesEpisode = value; } } + public string SeenCount { get { return _SeenCount; } set { _SeenCount = value; } } + } +} diff --git a/OpenSubtitlesHandler/Movies/CheckMovieHash2Result.cs b/OpenSubtitlesHandler/Movies/CheckMovieHash2Result.cs new file mode 100644 index 0000000000..e91541c3ce --- /dev/null +++ b/OpenSubtitlesHandler/Movies/CheckMovieHash2Result.cs @@ -0,0 +1,37 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; +using System.Collections.Generic; + +namespace OpenSubtitlesHandler +{ + public class CheckMovieHash2Result + { + private string name; + private List data = new List(); + + public string Name { get { return name; } set { name = value; } } + public List Items { get { return data; } set { data = value; } } + + public override string ToString() + { + return name; + } + } +} diff --git a/OpenSubtitlesHandler/Movies/CheckMovieHashResult.cs b/OpenSubtitlesHandler/Movies/CheckMovieHashResult.cs new file mode 100644 index 0000000000..b51181f237 --- /dev/null +++ b/OpenSubtitlesHandler/Movies/CheckMovieHashResult.cs @@ -0,0 +1,42 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; + +namespace OpenSubtitlesHandler +{ + public struct CheckMovieHashResult + { + private string name; + private string _MovieHash; + private string _MovieImdbID; + private string _MovieName; + private string _MovieYear; + + public string MovieHash { get { return _MovieHash; } set { _MovieHash = value; } } + public string MovieImdbID { get { return _MovieImdbID; } set { _MovieImdbID = value; } } + public string MovieName { get { return _MovieName; } set { _MovieName = value; } } + public string MovieYear { get { return _MovieYear; } set { _MovieYear = value; } } + public string Name { get { return name; } set { name = value; } } + + public override string ToString() + { + return name; + } + } +} diff --git a/OpenSubtitlesHandler/Movies/InsertMovieHashParameters.cs b/OpenSubtitlesHandler/Movies/InsertMovieHashParameters.cs new file mode 100644 index 0000000000..58402324b5 --- /dev/null +++ b/OpenSubtitlesHandler/Movies/InsertMovieHashParameters.cs @@ -0,0 +1,38 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +namespace OpenSubtitlesHandler +{ + public class InsertMovieHashParameters + { + private string _moviehash = ""; + private string _moviebytesize = ""; + private string _imdbid = ""; + private string _movietimems = ""; + private string _moviefps = ""; + private string _moviefilename = ""; + + public string moviehash { get { return _moviehash; } set { _moviehash = value; } } + public string moviebytesize { get { return _moviebytesize; } set { _moviebytesize = value; } } + public string imdbid { get { return _imdbid; } set { _imdbid = value; } } + public string movietimems { get { return _movietimems; } set { _movietimems = value; } } + public string moviefps { get { return _moviefps; } set { _moviefps = value; } } + public string moviefilename { get { return _moviefilename; } set { _moviefilename = value; } } + } +} diff --git a/OpenSubtitlesHandler/Movies/MovieSearchResult.cs b/OpenSubtitlesHandler/Movies/MovieSearchResult.cs new file mode 100644 index 0000000000..fe085acc43 --- /dev/null +++ b/OpenSubtitlesHandler/Movies/MovieSearchResult.cs @@ -0,0 +1,41 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; + +namespace OpenSubtitlesHandler +{ + public struct MovieSearchResult + { + private string id; + private string title; + + public string ID + { get { return id; } set { id = value; } } + public string Title + { get { return title; } set { title = value; } } + /// + /// Title + /// + /// + public override string ToString() + { + return title; + } + } +} diff --git a/OpenSubtitlesHandler/Movies/SearchToMailMovieParameter.cs b/OpenSubtitlesHandler/Movies/SearchToMailMovieParameter.cs new file mode 100644 index 0000000000..d5e3fc4c6b --- /dev/null +++ b/OpenSubtitlesHandler/Movies/SearchToMailMovieParameter.cs @@ -0,0 +1,30 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +namespace OpenSubtitlesHandler +{ + public struct SearchToMailMovieParameter + { + private string _moviehash; + private double _moviesize; + + public string moviehash { get { return _moviehash; } set { _moviehash = value; } } + public double moviesize { get { return _moviesize; } set { _moviesize = value; } } + } +} diff --git a/OpenSubtitlesHandler/OpenSubtitles.cs b/OpenSubtitlesHandler/OpenSubtitles.cs new file mode 100644 index 0000000000..ba3c461a1b --- /dev/null +++ b/OpenSubtitlesHandler/OpenSubtitles.cs @@ -0,0 +1,2387 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; +using System.Text; +using System.Collections.Generic; +using System.IO; +using OpenSubtitlesHandler.Console; +using XmlRpcHandler; + +namespace OpenSubtitlesHandler +{ + /// + /// The core of the OpenSubtitles Handler library. All members are static. + /// + public sealed class OpenSubtitles + { + private static string XML_PRC_USERAGENT = ""; + // This is session id after log in, important value and MUST be set by LogIn before any other call. + private static string TOKEN = ""; + + /// + /// Set the useragent value. This must be called before doing anything else. + /// + /// The useragent value + public static void SetUserAgent(string agent) + { + XML_PRC_USERAGENT = agent; + } + + /*Session handling*/ + /// + /// Send a LogIn request, this must be called before anything else in this class. + /// + /// The user name of OpenSubtitles + /// The password + /// The language, usally en + /// Status of the login operation + public static IMethodResponse LogIn(string userName, string password, string language) + { + // Method call .. + List parms = new List(); + parms.Add(new XmlRpcValueBasic(userName)); + parms.Add(new XmlRpcValueBasic(password)); + parms.Add(new XmlRpcValueBasic(language)); + parms.Add(new XmlRpcValueBasic(XML_PRC_USERAGENT)); + XmlRpcMethodCall call = new XmlRpcMethodCall("LogIn", parms); + OSHConsole.WriteLine("Sending LogIn request to the server ...", DebugCode.Good); + + //File.WriteAllText(".\\request.txt", Encoding.UTF8.GetString(XmlRpcGenerator.Generate(call))); + // Send the request to the server + string response = Utilities.GetStreamString(Utilities.SendRequest(XmlRpcGenerator.Generate(call), XML_PRC_USERAGENT)); + + if (!response.Contains("ERROR:")) + { + // No error occur, get and decode the response. We expect Struct here. + XmlRpcMethodCall[] calls = XmlRpcGenerator.DecodeMethodResponse(response); + if (calls.Length > 0) + { + if (calls[0].Parameters.Count > 0) + { + XmlRpcValueStruct mainStruct = (XmlRpcValueStruct)calls[0].Parameters[0]; + MethodResponseLogIn re = new MethodResponseLogIn("Success", "Log in successful."); + foreach (XmlRpcStructMember MEMBER in mainStruct.Members) + { + switch (MEMBER.Name) + { + case "token": re.Token = TOKEN = MEMBER.Data.Data.ToString(); OSHConsole.WriteLine(MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "seconds": re.Seconds = (double)MEMBER.Data.Data; OSHConsole.WriteLine(MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "status": re.Status = MEMBER.Data.Data.ToString(); OSHConsole.WriteLine(MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + } + } + return re; + } + } + } + else + { + OSHConsole.WriteLine(response, DebugCode.Error); + return new MethodResponseError("Fail", response); + } + return new MethodResponseError("Fail", "Log in failed !"); + } + /// + /// Log out from the server. Call this to terminate the session. + /// + /// + public static IMethodResponse LogOut() + { + if (TOKEN == "") + { + OSHConsole.WriteLine("Can't do this call, 'token' value not set. Please use Log In method first.", DebugCode.Error); + return new MethodResponseError("Fail", "Can't do this call, 'token' value not set. Please use Log In method first."); + } + // Method call .. + List parms = new List(); + parms.Add(new XmlRpcValueBasic(TOKEN, XmlRpcBasicValueType.String)); + XmlRpcMethodCall call = new XmlRpcMethodCall("LogOut", parms); + + OSHConsole.WriteLine("Sending LogOut request to the server ...", DebugCode.Good); + // Send the request to the server + string response = Utilities.GetStreamString(Utilities.SendRequest(XmlRpcGenerator.Generate(call), XML_PRC_USERAGENT)); + if (!response.Contains("ERROR:")) + { + // No error occur, get and decode the response. We expect Struct here. + XmlRpcMethodCall[] calls = XmlRpcGenerator.DecodeMethodResponse(response); + if (calls.Length > 0) + { + if (calls[0].Parameters.Count > 0) + { + XmlRpcValueStruct strct = (XmlRpcValueStruct)calls[0].Parameters[0]; + OSHConsole.WriteLine("STATUS=" + ((XmlRpcValueBasic)strct.Members[0].Data).Data.ToString()); + OSHConsole.WriteLine("SECONDS=" + ((XmlRpcValueBasic)strct.Members[1].Data).Data.ToString()); + MethodResponseLogIn re = new MethodResponseLogIn("Success", "Log out successful."); + re.Status = ((XmlRpcValueBasic)strct.Members[0].Data).Data.ToString(); + re.Seconds = (double)((XmlRpcValueBasic)strct.Members[1].Data).Data; + return re; + } + } + } + else + { + OSHConsole.WriteLine(response, DebugCode.Error); + return new MethodResponseError("Fail", response); + } + return new MethodResponseError("Fail", "Log out failed !"); + } + /// + /// keep-alive user's session, verify token/session validity + /// + /// Status of the call operation + public static IMethodResponse NoOperation() + { + if (TOKEN == "") + { + OSHConsole.WriteLine("Can't do this call, 'token' value not set. Please use Log In method first.", DebugCode.Error); + return new MethodResponseError("Fail", "Can't do this call, 'token' value not set. Please use Log In method first."); + } + // Method call .. + List parms = new List(); + parms.Add(new XmlRpcValueBasic(TOKEN, XmlRpcBasicValueType.String)); + parms.Add(new XmlRpcValueBasic(XML_PRC_USERAGENT, XmlRpcBasicValueType.String)); + XmlRpcMethodCall call = new XmlRpcMethodCall("NoOperation", parms); + + OSHConsole.WriteLine("Sending NoOperation request to the server ...", DebugCode.Good); + // Send the request to the server + string response = Utilities.GetStreamString(Utilities.SendRequest(XmlRpcGenerator.Generate(call), XML_PRC_USERAGENT)); + if (!response.Contains("ERROR:")) + { + // No error occur, get and decode the response. We expect Struct here. + XmlRpcMethodCall[] calls = XmlRpcGenerator.DecodeMethodResponse(response); + if (calls.Length > 0) + { + if (calls[0].Parameters.Count > 0) + { + MethodResponseNoOperation R = new MethodResponseNoOperation(); + XmlRpcValueStruct mainStruct = (XmlRpcValueStruct)calls[0].Parameters[0]; + // To make sure response is not currepted by server, do it in loop + foreach (XmlRpcStructMember MEMBER in mainStruct.Members) + { + switch (MEMBER.Name) + { + case "status": R.Status = (string)MEMBER.Data.Data; OSHConsole.WriteLine(MEMBER.Name + "= " + MEMBER.Data.Data); break; + case "seconds": R.Seconds = (double)MEMBER.Data.Data; OSHConsole.WriteLine(MEMBER.Name + "= " + MEMBER.Data.Data); break; + case "download_limits": + XmlRpcValueStruct dlStruct = (XmlRpcValueStruct)MEMBER.Data; + foreach (XmlRpcStructMember dlmember in dlStruct.Members) + { + OSHConsole.WriteLine(" >" + dlmember.Name + "= " + dlmember.Data.Data.ToString()); + switch (dlmember.Name) + { + case "global_wrh_download_limit": R.global_wrh_download_limit = dlmember.Data.Data.ToString(); break; + case "client_ip": R.client_ip = dlmember.Data.Data.ToString(); break; + case "limit_check_by": R.limit_check_by = dlmember.Data.Data.ToString(); break; + case "client_24h_download_count": R.client_24h_download_count = dlmember.Data.Data.ToString(); break; + case "client_downlaod_quota": R.client_downlaod_quota = dlmember.Data.Data.ToString(); break; + case "client_24h_download_limit": R.client_24h_download_limit = dlmember.Data.Data.ToString(); break; + } + } + break; + } + } + return R; + } + } + } + else + { + OSHConsole.WriteLine(response, DebugCode.Error); + return new MethodResponseError("Fail", response); + } + return new MethodResponseError("Fail", "NoOperation call failed !"); + } + /*Search and download*/ + /// + /// Search for subtitle files matching your videos using either video file hashes or IMDb IDs. + /// + /// List of search subtitle parameters which each one represents 'struct parameter' as descriped at http://trac.opensubtitles.org/projects/opensubtitles/wiki/XmlRpcSearchSubtitles + /// Status of the call operation. If the call success the response will be 'MethodResponseSubtitleSearch' + public static IMethodResponse SearchSubtitles(SubtitleSearchParameters[] parameters) + { + if (TOKEN == "") + { + OSHConsole.WriteLine("Can't do this call, 'token' value not set. Please use Log In method first.", DebugCode.Error); + return new MethodResponseError("Fail", "Can't do this call, 'token' value not set. Please use Log In method first."); + } + if (parameters == null) + { + OSHConsole.UpdateLine("No subtitle search parameter passed !!", DebugCode.Error); + return new MethodResponseError("Fail", "No subtitle search parameter passed"); ; + } + if (parameters.Length == 0) + { + OSHConsole.UpdateLine("No subtitle search parameter passed !!", DebugCode.Error); + return new MethodResponseError("Fail", "No subtitle search parameter passed"); ; + } + // Method call .. + List parms = new List(); + // Add token param + parms.Add(new XmlRpcValueBasic(TOKEN, XmlRpcBasicValueType.String)); + // Add subtitle search parameters. Each one will be like 'array' of structs. + XmlRpcValueArray array = new XmlRpcValueArray(); + foreach (SubtitleSearchParameters param in parameters) + { + XmlRpcValueStruct strct = new XmlRpcValueStruct(new List()); + // sublanguageid member + XmlRpcStructMember member = new XmlRpcStructMember("sublanguageid", + new XmlRpcValueBasic(param.SubLangaugeID, XmlRpcBasicValueType.String)); + strct.Members.Add(member); + // moviehash member + if (param.MovieHash.Length > 0 && param.MovieByteSize > 0) { + member = new XmlRpcStructMember("moviehash", + new XmlRpcValueBasic(param.MovieHash, XmlRpcBasicValueType.String)); + strct.Members.Add(member); + // moviehash member + member = new XmlRpcStructMember("moviebytesize", + new XmlRpcValueBasic(param.MovieByteSize, XmlRpcBasicValueType.Int)); + strct.Members.Add(member); + } + if (param.Query.Length > 0) { + member = new XmlRpcStructMember("query", + new XmlRpcValueBasic(param.Query, XmlRpcBasicValueType.String)); + strct.Members.Add(member); + } + + if (param.Episode.Length > 0 && param.Season.Length>0) { + member = new XmlRpcStructMember("season", + new XmlRpcValueBasic(param.Season, XmlRpcBasicValueType.String)); + strct.Members.Add(member); + member = new XmlRpcStructMember("episode", + new XmlRpcValueBasic(param.Episode, XmlRpcBasicValueType.String)); + strct.Members.Add(member); + } + + // imdbid member + if (param.IMDbID.Length > 0) + { + member = new XmlRpcStructMember("imdbid", + new XmlRpcValueBasic(param.IMDbID, XmlRpcBasicValueType.String)); + strct.Members.Add(member); + } + // Add the struct to the array + array.Values.Add(strct); + } + // Add the array to the parameters + parms.Add(array); + // Call ! + XmlRpcMethodCall call = new XmlRpcMethodCall("SearchSubtitles", parms); + OSHConsole.WriteLine("Sending SearchSubtitles request to the server ...", DebugCode.Good); + // Send the request to the server + string response = Utilities.GetStreamString(Utilities.SendRequest(XmlRpcGenerator.Generate(call), XML_PRC_USERAGENT)); + + if (!response.Contains("ERROR:")) + { + // No error occur, get and decode the response. + XmlRpcMethodCall[] calls = XmlRpcGenerator.DecodeMethodResponse(response); + if (calls.Length > 0) + { + if (calls[0].Parameters.Count > 0) + { + // We expect Struct of 3 members: + //* the first is status + //* the second is [array of structs, each one includes subtitle file]. + //* the third is [double basic value] represent seconds token by server. + XmlRpcValueStruct mainStruct = (XmlRpcValueStruct)calls[0].Parameters[0]; + // Create the response, we'll need it later + MethodResponseSubtitleSearch R = new MethodResponseSubtitleSearch(); + // To make sure response is not currepted by server, do it in loop + foreach (XmlRpcStructMember MEMBER in mainStruct.Members) + { + if (MEMBER.Name == "status") + { + R.Status = (string)MEMBER.Data.Data; + OSHConsole.WriteLine("Status= " + R.Status); + } + else if (MEMBER.Name == "seconds") + { + R.Seconds = (double)MEMBER.Data.Data; + OSHConsole.WriteLine("Seconds= " + R.Seconds); + } + else if (MEMBER.Name == "data") + { + if (MEMBER.Data is XmlRpcValueArray) + { + OSHConsole.WriteLine("Search results: "); + + XmlRpcValueArray rarray = (XmlRpcValueArray)MEMBER.Data; + foreach (IXmlRpcValue subStruct in rarray.Values) + { + if (subStruct == null) continue; + if (!(subStruct is XmlRpcValueStruct)) continue; + + SubtitleSearchResult result = new SubtitleSearchResult(); + foreach (XmlRpcStructMember submember in ((XmlRpcValueStruct)subStruct).Members) + { + // To avoid errors of arranged info or missing ones, let's do it with switch.. + switch (submember.Name) + { + case "IDMovie": result.IDMovie = submember.Data.Data.ToString(); break; + case "IDMovieImdb": result.IDMovieImdb = submember.Data.Data.ToString(); break; + case "IDSubMovieFile": result.IDSubMovieFile = submember.Data.Data.ToString(); break; + case "IDSubtitle": result.IDSubtitle = submember.Data.Data.ToString(); break; + case "IDSubtitleFile": result.IDSubtitleFile = submember.Data.Data.ToString(); break; + case "ISO639": result.ISO639 = submember.Data.Data.ToString(); break; + case "LanguageName": result.LanguageName = submember.Data.Data.ToString(); break; + case "MovieByteSize": result.MovieByteSize = submember.Data.Data.ToString(); break; + case "MovieHash": result.MovieHash = submember.Data.Data.ToString(); break; + case "MovieImdbRating": result.MovieImdbRating = submember.Data.Data.ToString(); break; + case "MovieName": result.MovieName = submember.Data.Data.ToString(); break; + case "MovieNameEng": result.MovieNameEng = submember.Data.Data.ToString(); break; + case "MovieReleaseName": result.MovieReleaseName = submember.Data.Data.ToString(); break; + case "MovieTimeMS": result.MovieTimeMS = submember.Data.Data.ToString(); break; + case "MovieYear": result.MovieYear = submember.Data.Data.ToString(); break; + case "SubActualCD": result.SubActualCD = submember.Data.Data.ToString(); break; + case "SubAddDate": result.SubAddDate = submember.Data.Data.ToString(); break; + case "SubAuthorComment": result.SubAuthorComment = submember.Data.Data.ToString(); break; + case "SubBad": result.SubBad = submember.Data.Data.ToString(); break; + case "SubDownloadLink": result.SubDownloadLink = submember.Data.Data.ToString(); break; + case "SubDownloadsCnt": result.SubDownloadsCnt = submember.Data.Data.ToString(); break; + case "SeriesEpisode": result.SeriesEpisode = submember.Data.Data.ToString(); break; + case "SeriesSeason": result.SeriesSeason = submember.Data.Data.ToString(); break; + case "SubFileName": result.SubFileName = submember.Data.Data.ToString(); break; + case "SubFormat": result.SubFormat = submember.Data.Data.ToString(); break; + case "SubHash": result.SubHash = submember.Data.Data.ToString(); break; + case "SubLanguageID": result.SubLanguageID = submember.Data.Data.ToString(); break; + case "SubRating": result.SubRating = submember.Data.Data.ToString(); break; + case "SubSize": result.SubSize = submember.Data.Data.ToString(); break; + case "SubSumCD": result.SubSumCD = submember.Data.Data.ToString(); break; + case "UserID": result.UserID = submember.Data.Data.ToString(); break; + case "UserNickName": result.UserNickName = submember.Data.Data.ToString(); break; + case "ZipDownloadLink": result.ZipDownloadLink = submember.Data.Data.ToString(); break; + } + } + R.Results.Add(result); + OSHConsole.WriteLine(">" + result.ToString()); + } + } + else// Unknown data ? + { + OSHConsole.WriteLine("Data= " + MEMBER.Data.Data.ToString(), DebugCode.Warning); + } + } + } + // Return the response to user !! + return R; + } + } + } + else + { + OSHConsole.WriteLine(response, DebugCode.Error); + return new MethodResponseError("Fail", response); + } + return new MethodResponseError("Fail", "Search Subtitles call failed !"); + } + /// + /// Download subtitle file(s) + /// + /// The subtitle IDS (an array of IDSubtitleFile value that given by server as SearchSubtiles results) + /// Status of the call operation. If the call success the response will be 'MethodResponseSubtitleDownload' which will hold downloaded subtitles + public static IMethodResponse DownloadSubtitles(int[] subIDS) + { + if (TOKEN == "") + { + OSHConsole.WriteLine("Can't do this call, 'token' value not set. Please use Log In method first.", DebugCode.Error); + return new MethodResponseError("Fail", "Can't do this call, 'token' value not set. Please use Log In method first."); + } + if (subIDS == null) + { + OSHConsole.UpdateLine("No subtitle id passed !!", DebugCode.Error); + return new MethodResponseError("Fail", "No subtitle id passed"); ; + } + if (subIDS.Length == 0) + { + OSHConsole.UpdateLine("No subtitle id passed !!", DebugCode.Error); + return new MethodResponseError("Fail", "No subtitle id passed"); ; + } + // Method call .. + List parms = new List(); + // Add token param + parms.Add(new XmlRpcValueBasic(TOKEN, XmlRpcBasicValueType.String)); + // Add subtitle search parameters. Each one will be like 'array' of structs. + XmlRpcValueArray array = new XmlRpcValueArray(); + foreach (int id in subIDS) + { + array.Values.Add(new XmlRpcValueBasic(id, XmlRpcBasicValueType.Int)); + } + // Add the array to the parameters + parms.Add(array); + // Call ! + XmlRpcMethodCall call = new XmlRpcMethodCall("DownloadSubtitles", parms); + OSHConsole.WriteLine("Sending DownloadSubtitles request to the server ...", DebugCode.Good); + // Send the request to the server + string response = Utilities.GetStreamString(Utilities.SendRequest(XmlRpcGenerator.Generate(call), XML_PRC_USERAGENT)); + if (!response.Contains("ERROR:")) + { + // No error occur, get and decode the response. + XmlRpcMethodCall[] calls = XmlRpcGenerator.DecodeMethodResponse(response); + if (calls.Length > 0) + { + if (calls[0].Parameters.Count > 0) + { + // We expect Struct of 3 members: + //* the first is status + //* the second is [array of structs, each one includes subtitle file]. + //* the third is [double basic value] represent seconds token by server. + XmlRpcValueStruct mainStruct = (XmlRpcValueStruct)calls[0].Parameters[0]; + // Create the response, we'll need it later + MethodResponseSubtitleDownload R = new MethodResponseSubtitleDownload(); + + // To make sure response is not currepted by server, do it in loop + foreach (XmlRpcStructMember MEMBER in mainStruct.Members) + { + if (MEMBER.Name == "status") + { + R.Status = (string)MEMBER.Data.Data; + OSHConsole.WriteLine("Status= " + R.Status); + } + else if (MEMBER.Name == "seconds") + { + R.Seconds = (double)MEMBER.Data.Data; + OSHConsole.WriteLine("Seconds= " + R.Seconds); + } + else if (MEMBER.Name == "data") + { + if (MEMBER.Data is XmlRpcValueArray) + { + OSHConsole.WriteLine("Download results:"); + XmlRpcValueArray rarray = (XmlRpcValueArray)MEMBER.Data; + foreach (IXmlRpcValue subStruct in rarray.Values) + { + if (subStruct == null) continue; + if (!(subStruct is XmlRpcValueStruct)) continue; + + SubtitleDownloadResult result = new SubtitleDownloadResult(); + foreach (XmlRpcStructMember submember in ((XmlRpcValueStruct)subStruct).Members) + { + // To avoid errors of arranged info or missing ones, let's do it with switch.. + switch (submember.Name) + { + case "idsubtitlefile": result.IdSubtitleFile = (string)submember.Data.Data; break; + case "data": result.Data = (string)submember.Data.Data; break; + } + } + R.Results.Add(result); + OSHConsole.WriteLine("> IDSubtilteFile= " + result.ToString()); + } + } + else// Unknown data ? + { + OSHConsole.WriteLine("Data= " + MEMBER.Data.Data.ToString(), DebugCode.Warning); + } + } + } + // Return the response to user !! + return R; + } + } + } + else + { + OSHConsole.WriteLine(response, DebugCode.Error); + return new MethodResponseError("Fail", response); + } + return new MethodResponseError("Fail", "DownloadSubtitles call failed !"); + } + /// + /// Returns comments for subtitles + /// + /// The subtitle IDS (an array of IDSubtitleFile value that given by server as SearchSubtiles results) + /// Status of the call operation. If the call success the response will be 'MethodResponseGetComments' + public static IMethodResponse GetComments(int[] subIDS) + { + if (TOKEN == "") + { + OSHConsole.WriteLine("Can't do this call, 'token' value not set. Please use Log In method first.", DebugCode.Error); + return new MethodResponseError("Fail", "Can't do this call, 'token' value not set. Please use Log In method first."); + } + if (subIDS == null) + { + OSHConsole.UpdateLine("No subtitle id passed !!", DebugCode.Error); + return new MethodResponseError("Fail", "No subtitle id passed"); ; + } + if (subIDS.Length == 0) + { + OSHConsole.UpdateLine("No subtitle id passed !!", DebugCode.Error); + return new MethodResponseError("Fail", "No subtitle id passed"); ; + } + // Method call .. + List parms = new List(); + // Add token param + parms.Add(new XmlRpcValueBasic(TOKEN)); + // Add subtitle search parameters. Each one will be like 'array' of structs. + XmlRpcValueArray array = new XmlRpcValueArray(subIDS); + // Add the array to the parameters + parms.Add(array); + // Call ! + XmlRpcMethodCall call = new XmlRpcMethodCall("GetComments", parms); + OSHConsole.WriteLine("Sending GetComments request to the server ...", DebugCode.Good); + // Send the request to the server + string response = Utilities.GetStreamString(Utilities.SendRequest(XmlRpcGenerator.Generate(call), XML_PRC_USERAGENT)); + if (!response.Contains("ERROR:")) + { + // No error occur, get and decode the response. + XmlRpcMethodCall[] calls = XmlRpcGenerator.DecodeMethodResponse(response); + if (calls.Length > 0) + { + if (calls[0].Parameters.Count > 0) + { + XmlRpcValueStruct mainStruct = (XmlRpcValueStruct)calls[0].Parameters[0]; + // Create the response, we'll need it later + MethodResponseGetComments R = new MethodResponseGetComments(); + + // To make sure response is not currepted by server, do it in loop + foreach (XmlRpcStructMember MEMBER in mainStruct.Members) + { + if (MEMBER.Name == "status") + { + R.Status = (string)MEMBER.Data.Data; + OSHConsole.WriteLine("Status= " + R.Status); + } + else if (MEMBER.Name == "seconds") + { + R.Seconds = (double)MEMBER.Data.Data; + OSHConsole.WriteLine("Seconds= " + R.Seconds); + } + else if (MEMBER.Name == "data") + { + if (MEMBER.Data is XmlRpcValueArray) + { + OSHConsole.WriteLine("Comments results:"); + XmlRpcValueArray rarray = (XmlRpcValueArray)MEMBER.Data; + foreach (IXmlRpcValue commentStruct in rarray.Values) + { + if (commentStruct == null) continue; + if (!(commentStruct is XmlRpcValueStruct)) continue; + + GetCommentsResult result = new GetCommentsResult(); + foreach (XmlRpcStructMember commentmember in ((XmlRpcValueStruct)commentStruct).Members) + { + // To avoid errors of arranged info or missing ones, let's do it with switch.. + switch (commentmember.Name) + { + case "IDSubtitle": result.IDSubtitle = (string)commentmember.Data.Data; break; + case "UserID": result.UserID = (string)commentmember.Data.Data; break; + case "UserNickName": result.UserNickName = (string)commentmember.Data.Data; break; + case "Comment": result.Comment = (string)commentmember.Data.Data; break; + case "Created": result.Created = (string)commentmember.Data.Data; break; + } + } + R.Results.Add(result); + OSHConsole.WriteLine("> IDSubtitle= " + result.ToString()); + } + } + else// Unknown data ? + { + OSHConsole.WriteLine("Data= " + MEMBER.Data.Data.ToString(), DebugCode.Warning); + } + } + } + // Return the response to user !! + return R; + } + } + } + else + { + OSHConsole.WriteLine(response, DebugCode.Error); + return new MethodResponseError("Fail", response); + } + return new MethodResponseError("Fail", "GetComments call failed !"); + } + + /// + /// Schedule a periodical search for subtitles matching given video files, send results to user's e-mail address. + /// + /// The language 3 lenght ids array + /// The movies parameters + /// Status of the call operation. If the call success the response will be 'MethodResponseSearchToMail' + public static IMethodResponse SearchToMail(string[] languageIDS, SearchToMailMovieParameter[] movies) + { + if (TOKEN == "") + { + OSHConsole.WriteLine("Can't do this call, 'token' value not set. Please use Log In method first.", DebugCode.Error); + return new MethodResponseError("Fail", "Can't do this call, 'token' value not set. Please use Log In method first."); + } + // Method call .. + List parms = new List(); + parms.Add(new XmlRpcValueBasic(TOKEN, XmlRpcBasicValueType.String)); + // Array of sub langs + XmlRpcValueArray a = new XmlRpcValueArray(languageIDS); + parms.Add(a); + // Array of video parameters + a = new XmlRpcValueArray(); + foreach (SearchToMailMovieParameter p in movies) + { + XmlRpcValueStruct str = new XmlRpcValueStruct(new List()); + str.Members.Add(new XmlRpcStructMember("moviehash", new XmlRpcValueBasic(p.moviehash))); + str.Members.Add(new XmlRpcStructMember("moviesize", new XmlRpcValueBasic(p.moviesize))); + a.Values.Add(str); + } + parms.Add(a); + XmlRpcMethodCall call = new XmlRpcMethodCall("SearchToMail", parms); + + OSHConsole.WriteLine("Sending SearchToMail request to the server ...", DebugCode.Good); + // Send the request to the server + string response = Utilities.GetStreamString(Utilities.SendRequest(XmlRpcGenerator.Generate(call), XML_PRC_USERAGENT)); + if (!response.Contains("ERROR:")) + { + // No error occur, get and decode the response. + XmlRpcMethodCall[] calls = XmlRpcGenerator.DecodeMethodResponse(response); + if (calls.Length > 0) + { + if (calls[0].Parameters.Count > 0) + { + XmlRpcValueStruct mainStruct = (XmlRpcValueStruct)calls[0].Parameters[0]; + // Create the response, we'll need it later + MethodResponseSearchToMail R = new MethodResponseSearchToMail(); + + // To make sure response is not currepted by server, do it in loop + foreach (XmlRpcStructMember MEMBER in mainStruct.Members) + { + switch (MEMBER.Name) + { + case "status": R.Status = (string)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "seconds": R.Seconds = (double)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + } + } + // Return the response to user !! + return R; + } + } + } + else + { + OSHConsole.WriteLine(response, DebugCode.Error); + return new MethodResponseError("Fail", response); + } + return new MethodResponseError("Fail", "SearchToMail call failed !"); + } + /*Movies*/ + /// + /// Search for a movie (using movie title) + /// + /// Movie title user is searching for, this is cleaned-up a bit (remove dvdrip, etc.) before searching + /// Status of the call operation. If the call success the response will be 'MethodResponseSubtitleSearch' + public static IMethodResponse SearchMoviesOnIMDB(string query) + { + if (TOKEN == "") + { + OSHConsole.WriteLine("Can't do this call, 'token' value not set. Please use Log In method first.", DebugCode.Error); + return new MethodResponseError("Fail", "Can't do this call, 'token' value not set. Please use Log In method first."); + } + // Method call .. + List parms = new List(); + // Add token param + parms.Add(new XmlRpcValueBasic(TOKEN, XmlRpcBasicValueType.String)); + // Add query param + parms.Add(new XmlRpcValueBasic(query, XmlRpcBasicValueType.String)); + // Call ! + XmlRpcMethodCall call = new XmlRpcMethodCall("SearchMoviesOnIMDB", parms); + OSHConsole.WriteLine("Sending SearchMoviesOnIMDB request to the server ...", DebugCode.Good); + // Send the request to the server + string response = Utilities.GetStreamString(Utilities.SendRequest(XmlRpcGenerator.Generate(call), XML_PRC_USERAGENT)); + if (!response.Contains("ERROR:")) + { + // No error occur, get and decode the response. + XmlRpcMethodCall[] calls = XmlRpcGenerator.DecodeMethodResponse(response); + if (calls.Length > 0) + { + if (calls[0].Parameters.Count > 0) + { + XmlRpcValueStruct mainStruct = (XmlRpcValueStruct)calls[0].Parameters[0]; + // Create the response, we'll need it later + MethodResponseMovieSearch R = new MethodResponseMovieSearch(); + + // To make sure response is not currepted by server, do it in loop + foreach (XmlRpcStructMember MEMBER in mainStruct.Members) + { + if (MEMBER.Name == "status") + { + R.Status = (string)MEMBER.Data.Data; + OSHConsole.WriteLine("Status= " + R.Status); + } + else if (MEMBER.Name == "seconds") + { + R.Seconds = (double)MEMBER.Data.Data; + OSHConsole.WriteLine("Seconds= " + R.Seconds); + } + else if (MEMBER.Name == "data") + { + if (MEMBER.Data is XmlRpcValueArray) + { + OSHConsole.WriteLine("Search results:"); + XmlRpcValueArray rarray = (XmlRpcValueArray)MEMBER.Data; + foreach (IXmlRpcValue subStruct in rarray.Values) + { + if (subStruct == null) continue; + if (!(subStruct is XmlRpcValueStruct)) continue; + + MovieSearchResult result = new MovieSearchResult(); + foreach (XmlRpcStructMember submember in ((XmlRpcValueStruct)subStruct).Members) + { + // To avoid errors of arranged info or missing ones, let's do it with switch.. + switch (submember.Name) + { + case "id": result.ID = (string)submember.Data.Data; break; + case "title": result.Title = (string)submember.Data.Data; break; + } + } + R.Results.Add(result); + OSHConsole.WriteLine(">" + result.ToString()); + } + } + else// Unknown data ? + { + OSHConsole.WriteLine("Data= " + MEMBER.Data.Data.ToString(), DebugCode.Warning); + } + } + } + // Return the response to user !! + return R; + } + } + } + else + { + OSHConsole.WriteLine(response, DebugCode.Error); + return new MethodResponseError("Fail", response); + } + return new MethodResponseError("Fail", "SearchMoviesOnIMDB call failed !"); + } + /// + /// Get movie details for given IMDb ID + /// + /// http://www.imdb.com/ + /// Status of the call operation. If the call success the response will be 'MethodResponseMovieDetails' + public static IMethodResponse GetIMDBMovieDetails(string imdbid) + { + if (TOKEN == "") + { + OSHConsole.WriteLine("Can't do this call, 'token' value not set. Please use Log In method first.", DebugCode.Error); + return new MethodResponseError("Fail", "Can't do this call, 'token' value not set. Please use Log In method first."); + } + // Method call .. + List parms = new List(); + // Add token param + parms.Add(new XmlRpcValueBasic(TOKEN)); + // Add query param + parms.Add(new XmlRpcValueBasic(imdbid)); + // Call ! + XmlRpcMethodCall call = new XmlRpcMethodCall("GetIMDBMovieDetails", parms); + OSHConsole.WriteLine("Sending GetIMDBMovieDetails request to the server ...", DebugCode.Good); + // Send the request to the server + string response = Utilities.GetStreamString(Utilities.SendRequest(XmlRpcGenerator.Generate(call), XML_PRC_USERAGENT)); + if (!response.Contains("ERROR:")) + { + // No error occur, get and decode the response. + XmlRpcMethodCall[] calls = XmlRpcGenerator.DecodeMethodResponse(response); + if (calls.Length > 0) + { + if (calls[0].Parameters.Count > 0) + { + XmlRpcValueStruct mainStruct = (XmlRpcValueStruct)calls[0].Parameters[0]; + // Create the response, we'll need it later + MethodResponseMovieDetails R = new MethodResponseMovieDetails(); + + // To make sure response is not currepted by server, do it in loop + foreach (XmlRpcStructMember MEMBER in mainStruct.Members) + { + if (MEMBER.Name == "status") + { + R.Status = (string)MEMBER.Data.Data; + OSHConsole.WriteLine("Status= " + R.Status); + } + else if (MEMBER.Name == "seconds") + { + R.Seconds = (double)MEMBER.Data.Data; + OSHConsole.WriteLine("Seconds= " + R.Seconds); + } + else if (MEMBER.Name == "data") + { + // We expect struct with details... + if (MEMBER.Data is XmlRpcValueStruct) + { + OSHConsole.WriteLine("Details result:"); + XmlRpcValueStruct detailsStruct = (XmlRpcValueStruct)MEMBER.Data; + foreach (XmlRpcStructMember dmem in detailsStruct.Members) + { + switch (dmem.Name) + { + case "id": R.ID = dmem.Data.Data.ToString(); OSHConsole.WriteLine(">" + dmem.Name + "= " + dmem.Data.Data.ToString()); break; + case "title": R.Title = dmem.Data.Data.ToString(); OSHConsole.WriteLine(">" + dmem.Name + "= " + dmem.Data.Data.ToString()); break; + case "year": R.Year = dmem.Data.Data.ToString(); OSHConsole.WriteLine(">" + dmem.Name + "= " + dmem.Data.Data.ToString()); break; + case "cover": R.CoverLink = dmem.Data.Data.ToString(); OSHConsole.WriteLine(">" + dmem.Name + "= " + dmem.Data.Data.ToString()); break; + case "duration": R.Duration = dmem.Data.Data.ToString(); OSHConsole.WriteLine(">" + dmem.Name + "= " + dmem.Data.Data.ToString()); break; + case "tagline": R.Tagline = dmem.Data.Data.ToString(); OSHConsole.WriteLine(">" + dmem.Name + "= " + dmem.Data.Data.ToString()); break; + case "plot": R.Plot = dmem.Data.Data.ToString(); OSHConsole.WriteLine(">" + dmem.Name + "= " + dmem.Data.Data.ToString()); break; + case "goofs": R.Goofs = dmem.Data.Data.ToString(); OSHConsole.WriteLine(">" + dmem.Name + "= " + dmem.Data.Data.ToString()); break; + case "trivia": R.Trivia = dmem.Data.Data.ToString(); OSHConsole.WriteLine(">" + dmem.Name + "= " + dmem.Data.Data.ToString()); break; + case "cast": + // this is another struct with cast members... + OSHConsole.WriteLine(">" + dmem.Name + "= "); + XmlRpcValueStruct castStruct = (XmlRpcValueStruct)dmem.Data; + foreach (XmlRpcStructMember castMemeber in castStruct.Members) + { + R.Cast.Add(castMemeber.Data.Data.ToString()); + OSHConsole.WriteLine(" >" + castMemeber.Data.Data.ToString()); + } + break; + case "directors": + OSHConsole.WriteLine(">" + dmem.Name + "= "); + // this is another struct with directors members... + XmlRpcValueStruct directorsStruct = (XmlRpcValueStruct)dmem.Data; + foreach (XmlRpcStructMember directorsMember in directorsStruct.Members) + { + R.Directors.Add(directorsMember.Data.Data.ToString()); + OSHConsole.WriteLine(" >" + directorsMember.Data.Data.ToString()); + } + break; + case "writers": + OSHConsole.WriteLine(">" + dmem.Name + "= "); + // this is another struct with writers members... + XmlRpcValueStruct writersStruct = (XmlRpcValueStruct)dmem.Data; + foreach (XmlRpcStructMember writersMember in writersStruct.Members) + { + R.Writers.Add(writersMember.Data.Data.ToString()); + OSHConsole.WriteLine("+->" + writersMember.Data.Data.ToString()); + } + break; + case "awards": + // this is an array of genres... + XmlRpcValueArray awardsArray = (XmlRpcValueArray)dmem.Data; + foreach (XmlRpcValueBasic award in awardsArray.Values) + { + R.Awards.Add(award.Data.ToString()); + OSHConsole.WriteLine(" >" + award.Data.ToString()); + } + break; + case "genres": + OSHConsole.WriteLine(">" + dmem.Name + "= "); + // this is an array of genres... + XmlRpcValueArray genresArray = (XmlRpcValueArray)dmem.Data; + foreach (XmlRpcValueBasic genre in genresArray.Values) + { + R.Genres.Add(genre.Data.ToString()); + OSHConsole.WriteLine(" >" + genre.Data.ToString()); + } + break; + case "country": + OSHConsole.WriteLine(">" + dmem.Name + "= "); + // this is an array of country... + XmlRpcValueArray countryArray = (XmlRpcValueArray)dmem.Data; + foreach (XmlRpcValueBasic country in countryArray.Values) + { + R.Country.Add(country.Data.ToString()); + OSHConsole.WriteLine(" >" + country.Data.ToString()); + } + break; + case "language": + OSHConsole.WriteLine(">" + dmem.Name + "= "); + // this is an array of language... + XmlRpcValueArray languageArray = (XmlRpcValueArray)dmem.Data; + foreach (XmlRpcValueBasic language in languageArray.Values) + { + R.Language.Add(language.Data.ToString()); + OSHConsole.WriteLine(" >" + language.Data.ToString()); + } + break; + case "certification": + OSHConsole.WriteLine(">" + dmem.Name + "= "); + // this is an array of certification... + XmlRpcValueArray certificationArray = (XmlRpcValueArray)dmem.Data; + foreach (XmlRpcValueBasic certification in certificationArray.Values) + { + R.Certification.Add(certification.Data.ToString()); + OSHConsole.WriteLine(" >" + certification.Data.ToString()); + } + break; + } + } + } + else// Unknown data ? + { + OSHConsole.WriteLine("Data= " + MEMBER.Data.Data.ToString(), DebugCode.Warning); + } + } + } + // Return the response to user !! + return R; + } + } + } + else + { + OSHConsole.WriteLine(response, DebugCode.Error); + return new MethodResponseError("Fail", response); + } + return new MethodResponseError("Fail", "GetIMDBMovieDetails call failed !"); + } + /// + /// Allows registered users to insert new movies (not stored in IMDb) to the database. + /// + /// Movie title + /// Release year + /// Status of the call operation. If the call success the response will be 'MethodResponseInsertMovie' + public static IMethodResponse InsertMovie(string movieName, string movieyear) + { + if (TOKEN == "") + { + OSHConsole.WriteLine("Can't do this call, 'token' value not set. Please use Log In method first.", DebugCode.Error); + return new MethodResponseError("Fail", "Can't do this call, 'token' value not set. Please use Log In method first."); + } + // Method call .. + List parms = new List(); + // Add token param + parms.Add(new XmlRpcValueBasic(TOKEN, XmlRpcBasicValueType.String)); + // Add movieinfo struct + XmlRpcValueStruct movieinfo = new XmlRpcValueStruct(new List()); + movieinfo.Members.Add(new XmlRpcStructMember("moviename", new XmlRpcValueBasic(movieName))); + movieinfo.Members.Add(new XmlRpcStructMember("movieyear", new XmlRpcValueBasic(movieyear))); + parms.Add(movieinfo); + // Call ! + XmlRpcMethodCall call = new XmlRpcMethodCall("InsertMovie", parms); + OSHConsole.WriteLine("Sending InsertMovie request to the server ...", DebugCode.Good); + // Send the request to the server + string response = Utilities.GetStreamString(Utilities.SendRequest(XmlRpcGenerator.Generate(call), XML_PRC_USERAGENT)); + if (!response.Contains("ERROR:")) + { + // No error occur, get and decode the response. + XmlRpcMethodCall[] calls = XmlRpcGenerator.DecodeMethodResponse(response); + if (calls.Length > 0) + { + if (calls[0].Parameters.Count > 0) + { + XmlRpcValueStruct mainStruct = (XmlRpcValueStruct)calls[0].Parameters[0]; + // Create the response, we'll need it later + MethodResponseInsertMovie R = new MethodResponseInsertMovie(); + + // To make sure response is not currepted by server, do it in loop + foreach (XmlRpcStructMember MEMBER in mainStruct.Members) + { + if (MEMBER.Name == "status") + { + R.Status = (string)MEMBER.Data.Data; + OSHConsole.WriteLine("Status= " + R.Status); + } + else if (MEMBER.Name == "seconds") + { + R.Seconds = (double)MEMBER.Data.Data; + OSHConsole.WriteLine("Seconds= " + R.Seconds); + } + else if (MEMBER.Name == "id") + { + R.ID = (string)MEMBER.Data.Data; + OSHConsole.WriteLine("ID= " + R.Seconds); + } + } + // Return the response to user !! + return R; + } + } + } + else + { + OSHConsole.WriteLine(response, DebugCode.Error); + return new MethodResponseError("Fail", response); + } + return new MethodResponseError("Fail", "InsertMovie call failed !"); + } + /// + /// Inserts or updates data to tables, which are used for CheckMovieHash() and !CheckMovieHash2(). + /// + /// The parameters + /// Status of the call operation. If the call success the response will be 'MethodResponseInsertMovieHash' + public static IMethodResponse InsertMovieHash(InsertMovieHashParameters[] parameters) + { + if (TOKEN == "") + { + OSHConsole.WriteLine("Can't do this call, 'token' value not set. Please use Log In method first.", DebugCode.Error); + return new MethodResponseError("Fail", "Can't do this call, 'token' value not set. Please use Log In method first."); + } + // Method call .. + List parms = new List(); + parms.Add(new XmlRpcValueBasic(TOKEN, XmlRpcBasicValueType.String)); + foreach (InsertMovieHashParameters p in parameters) + { + XmlRpcValueStruct pstruct = new XmlRpcValueStruct(new List()); + pstruct.Members.Add(new XmlRpcStructMember("moviehash", new XmlRpcValueBasic(p.moviehash))); + pstruct.Members.Add(new XmlRpcStructMember("moviebytesize", new XmlRpcValueBasic(p.moviebytesize))); + pstruct.Members.Add(new XmlRpcStructMember("imdbid", new XmlRpcValueBasic(p.imdbid))); + pstruct.Members.Add(new XmlRpcStructMember("movietimems", new XmlRpcValueBasic(p.movietimems))); + pstruct.Members.Add(new XmlRpcStructMember("moviefps", new XmlRpcValueBasic(p.moviefps))); + pstruct.Members.Add(new XmlRpcStructMember("moviefilename", new XmlRpcValueBasic(p.moviefilename))); + parms.Add(pstruct); + } + XmlRpcMethodCall call = new XmlRpcMethodCall("InsertMovieHash", parms); + + OSHConsole.WriteLine("Sending InsertMovieHash request to the server ...", DebugCode.Good); + // Send the request to the server + string response = Utilities.GetStreamString(Utilities.SendRequest(XmlRpcGenerator.Generate(call), XML_PRC_USERAGENT)); + if (!response.Contains("ERROR:")) + { + // No error occur, get and decode the response. + XmlRpcMethodCall[] calls = XmlRpcGenerator.DecodeMethodResponse(response); + if (calls.Length > 0) + { + if (calls[0].Parameters.Count > 0) + { + XmlRpcValueStruct mainStruct = (XmlRpcValueStruct)calls[0].Parameters[0]; + // Create the response, we'll need it later + MethodResponseInsertMovieHash R = new MethodResponseInsertMovieHash(); + + // To make sure response is not currepted by server, do it in loop + foreach (XmlRpcStructMember MEMBER in mainStruct.Members) + { + switch (MEMBER.Name) + { + case "status": + R.Status = (string)MEMBER.Data.Data; + OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); + break; + case "seconds": + R.Seconds = (double)MEMBER.Data.Data; + OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); + break; + case "data": + XmlRpcValueStruct dataStruct = (XmlRpcValueStruct)MEMBER.Data; + foreach (XmlRpcStructMember dataMember in dataStruct.Members) + { + switch (dataMember.Name) + { + case "accepted_moviehashes": + XmlRpcValueArray mh = (XmlRpcValueArray)dataMember.Data; + foreach (IXmlRpcValue val in mh.Values) + { + if (val is XmlRpcValueBasic) + { + R.accepted_moviehashes.Add(val.Data.ToString()); + } + } + break; + case "new_imdbs": + XmlRpcValueArray mi = (XmlRpcValueArray)dataMember.Data; + foreach (IXmlRpcValue val in mi.Values) + { + if (val is XmlRpcValueBasic) + { + R.new_imdbs.Add(val.Data.ToString()); + } + } + break; + } + } + break; + } + } + // Return the response to user !! + return R; + } + } + } + else + { + OSHConsole.WriteLine(response, DebugCode.Error); + return new MethodResponseError("Fail", response); + } + return new MethodResponseError("Fail", "InsertMovieHash call failed !"); + } + /*Reporting and rating*/ + /// + /// Get basic server information and statistics + /// + /// Status of the call operation. If the call success the response will be 'MethodResponseServerInfo' + public static IMethodResponse ServerInfo() + { + if (TOKEN == "") + { + OSHConsole.WriteLine("Can't do this call, 'token' value not set. Please use Log In method first.", DebugCode.Error); + return new MethodResponseError("Fail", "Can't do this call, 'token' value not set. Please use Log In method first."); + } + // Method call .. + List parms = new List(); + parms.Add(new XmlRpcValueBasic(TOKEN, XmlRpcBasicValueType.String)); + parms.Add(new XmlRpcValueBasic(XML_PRC_USERAGENT, XmlRpcBasicValueType.String)); + XmlRpcMethodCall call = new XmlRpcMethodCall("ServerInfo", parms); + + OSHConsole.WriteLine("Sending ServerInfo request to the server ...", DebugCode.Good); + // Send the request to the server + string response = Utilities.GetStreamString(Utilities.SendRequest(XmlRpcGenerator.Generate(call), XML_PRC_USERAGENT)); + if (!response.Contains("ERROR:")) + { + // No error occur, get and decode the response. + XmlRpcMethodCall[] calls = XmlRpcGenerator.DecodeMethodResponse(response); + if (calls.Length > 0) + { + if (calls[0].Parameters.Count > 0) + { + XmlRpcValueStruct mainStruct = (XmlRpcValueStruct)calls[0].Parameters[0]; + // Create the response, we'll need it later + MethodResponseServerInfo R = new MethodResponseServerInfo(); + + // To make sure response is not currepted by server, do it in loop + foreach (XmlRpcStructMember MEMBER in mainStruct.Members) + { + switch (MEMBER.Name) + { + case "status": + R.Status = (string)MEMBER.Data.Data; + OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); + break; + case "seconds": + R.Seconds = (double)MEMBER.Data.Data; + OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); + break; + case "xmlrpc_version": + R.xmlrpc_version = (string)MEMBER.Data.Data; + OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); + break; + case "xmlrpc_url": + R.xmlrpc_url = (string)MEMBER.Data.Data; + OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); + break; + case "application": + R.application = (string)MEMBER.Data.Data; + OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); + break; + case "contact": + R.contact = (string)MEMBER.Data.Data; + OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); + break; + case "website_url": + R.website_url = (string)MEMBER.Data.Data; + OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); + break; + case "users_online_total": + R.users_online_total = (int)MEMBER.Data.Data; + OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); + break; + case "users_online_program": + R.users_online_program = (int)MEMBER.Data.Data; + OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); + break; + case "users_loggedin": + R.users_loggedin = (int)MEMBER.Data.Data; + OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); + break; + case "users_max_alltime": + R.users_max_alltime = (string)MEMBER.Data.Data; + OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); + break; + case "users_registered": + R.users_registered = (string)MEMBER.Data.Data; + OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); + break; + case "subs_downloads": + R.subs_downloads = (string)MEMBER.Data.Data; + OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); + break; + case "subs_subtitle_files": + R.subs_subtitle_files = (string)MEMBER.Data.Data; + OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); + break; + case "movies_total": + R.movies_total = (string)MEMBER.Data.Data; + OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); + break; + case "movies_aka": + R.movies_aka = (string)MEMBER.Data.Data; + OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); + break; + case "total_subtitles_languages": + R.total_subtitles_languages = (string)MEMBER.Data.Data; + OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); + break; + case "last_update_strings": + //R.total_subtitles_languages = (string)MEMBER.Data.Data; + OSHConsole.WriteLine(">" + MEMBER.Name + ":"); + XmlRpcValueStruct luStruct = (XmlRpcValueStruct)MEMBER.Data; + foreach (XmlRpcStructMember luMemeber in luStruct.Members) + { + R.last_update_strings.Add(luMemeber.Name + " [" + luMemeber.Data.Data.ToString() + "]"); + OSHConsole.WriteLine(" >" + luMemeber.Name + "= " + luMemeber.Data.Data.ToString()); + } + break; + } + } + // Return the response to user !! + return R; + } + } + } + else + { + OSHConsole.WriteLine(response, DebugCode.Error); + return new MethodResponseError("Fail", response); + } + return new MethodResponseError("Fail", "ServerInfo call failed !"); + } + /// + /// Report wrong subtitle file <--> video file combination + /// + /// Identifier of the subtitle file <--> video file combination + /// Status of the call operation. If the call success the response will be 'MethodResponseReportWrongMovieHash' + public static IMethodResponse ReportWrongMovieHash(string IDSubMovieFile) + { + if (TOKEN == "") + { + OSHConsole.WriteLine("Can't do this call, 'token' value not set. Please use Log In method first.", DebugCode.Error); + return new MethodResponseError("Fail", "Can't do this call, 'token' value not set. Please use Log In method first."); + } + // Method call .. + List parms = new List(); + parms.Add(new XmlRpcValueBasic(TOKEN, XmlRpcBasicValueType.String)); + parms.Add(new XmlRpcValueBasic(IDSubMovieFile, XmlRpcBasicValueType.String)); + XmlRpcMethodCall call = new XmlRpcMethodCall("ReportWrongMovieHash", parms); + + OSHConsole.WriteLine("Sending ReportWrongMovieHash request to the server ...", DebugCode.Good); + // Send the request to the server + string response = Utilities.GetStreamString(Utilities.SendRequest(XmlRpcGenerator.Generate(call), XML_PRC_USERAGENT)); + if (!response.Contains("ERROR:")) + { + // No error occur, get and decode the response. + XmlRpcMethodCall[] calls = XmlRpcGenerator.DecodeMethodResponse(response); + if (calls.Length > 0) + { + if (calls[0].Parameters.Count > 0) + { + XmlRpcValueStruct mainStruct = (XmlRpcValueStruct)calls[0].Parameters[0]; + // Create the response, we'll need it later + MethodResponseReportWrongMovieHash R = new MethodResponseReportWrongMovieHash(); + + // To make sure response is not currepted by server, do it in loop + foreach (XmlRpcStructMember MEMBER in mainStruct.Members) + { + switch (MEMBER.Name) + { + case "status": + R.Status = (string)MEMBER.Data.Data; + OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); + break; + case "seconds": + R.Seconds = (double)MEMBER.Data.Data; + OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); + break; + } + } + // Return the response to user !! + return R; + } + } + } + else + { + OSHConsole.WriteLine(response, DebugCode.Error); + return new MethodResponseError("Fail", response); + } + return new MethodResponseError("Fail", "ReportWrongMovieHash call failed !"); + } + /// + /// This method is needed to report bad movie hash for imdbid. This method should be used for correcting wrong entries, + /// when using CheckMovieHash2. Pass moviehash and moviebytesize for file, and imdbid as new, corrected one IMDBID + /// (id number without trailing zeroes). After some reports, moviehash will be linked to new imdbid. + /// + /// The movie hash + /// The movie size in bytes + /// The movie imbid + /// Status of the call operation. If the call success the response will be 'MethodResponseReportWrongImdbMovie' + public static IMethodResponse ReportWrongImdbMovie(string moviehash, string moviebytesize, string imdbid) + { + if (TOKEN == "") + { + OSHConsole.WriteLine("Can't do this call, 'token' value not set. Please use Log In method first.", DebugCode.Error); + return new MethodResponseError("Fail", "Can't do this call, 'token' value not set. Please use Log In method first."); + } + // Method call .. + List parms = new List(); + parms.Add(new XmlRpcValueBasic(TOKEN, XmlRpcBasicValueType.String)); + XmlRpcValueStruct s = new XmlRpcValueStruct(new List()); + s.Members.Add(new XmlRpcStructMember("moviehash", new XmlRpcValueBasic(moviehash))); + s.Members.Add(new XmlRpcStructMember("moviebytesize", new XmlRpcValueBasic(moviebytesize))); + s.Members.Add(new XmlRpcStructMember("imdbid", new XmlRpcValueBasic(imdbid))); + parms.Add(s); + XmlRpcMethodCall call = new XmlRpcMethodCall("ReportWrongImdbMovie", parms); + + OSHConsole.WriteLine("Sending ReportWrongImdbMovie request to the server ...", DebugCode.Good); + // Send the request to the server + string response = Utilities.GetStreamString(Utilities.SendRequest(XmlRpcGenerator.Generate(call), XML_PRC_USERAGENT)); + if (!response.Contains("ERROR:")) + { + // No error occur, get and decode the response. + XmlRpcMethodCall[] calls = XmlRpcGenerator.DecodeMethodResponse(response); + if (calls.Length > 0) + { + if (calls[0].Parameters.Count > 0) + { + XmlRpcValueStruct mainStruct = (XmlRpcValueStruct)calls[0].Parameters[0]; + // Create the response, we'll need it later + MethodResponseAddComment R = new MethodResponseAddComment(); + + // To make sure response is not currepted by server, do it in loop + foreach (XmlRpcStructMember MEMBER in mainStruct.Members) + { + switch (MEMBER.Name) + { + case "status": R.Status = (string)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "seconds": R.Seconds = (double)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + } + } + // Return the response to user !! + return R; + } + } + } + else + { + OSHConsole.WriteLine(response, DebugCode.Error); + return new MethodResponseError("Fail", response); + } + return new MethodResponseError("Fail", "ReportWrongImdbMovie call failed !"); + } + /// + /// Rate subtitles + /// + /// Id of subtitle (NOT subtitle file) user wants to rate + /// Subtitle rating, must be in interval 1 (worst) to 10 (best). + /// Status of the call operation. If the call success the response will be 'MethodResponseSubtitlesVote' + public static IMethodResponse SubtitlesVote(int idsubtitle, int score) + { + if (TOKEN == "") + { + OSHConsole.WriteLine("Can't do this call, 'token' value not set. Please use Log In method first.", DebugCode.Error); + return new MethodResponseError("Fail", "Can't do this call, 'token' value not set. Please use Log In method first."); + } + // Method call .. + List parms = new List(); + parms.Add(new XmlRpcValueBasic(TOKEN, XmlRpcBasicValueType.String)); + XmlRpcValueStruct s = new XmlRpcValueStruct(new List()); + s.Members.Add(new XmlRpcStructMember("idsubtitle", new XmlRpcValueBasic(idsubtitle))); + s.Members.Add(new XmlRpcStructMember("score", new XmlRpcValueBasic(score))); + parms.Add(s); + XmlRpcMethodCall call = new XmlRpcMethodCall("SubtitlesVote", parms); + + OSHConsole.WriteLine("Sending SubtitlesVote request to the server ...", DebugCode.Good); + // Send the request to the server + string response = Utilities.GetStreamString(Utilities.SendRequest(XmlRpcGenerator.Generate(call), XML_PRC_USERAGENT)); + if (!response.Contains("ERROR:")) + { + // No error occur, get and decode the response. + XmlRpcMethodCall[] calls = XmlRpcGenerator.DecodeMethodResponse(response); + if (calls.Length > 0) + { + if (calls[0].Parameters.Count > 0) + { + XmlRpcValueStruct mainStruct = (XmlRpcValueStruct)calls[0].Parameters[0]; + // Create the response, we'll need it later + MethodResponseSubtitlesVote R = new MethodResponseSubtitlesVote(); + + // To make sure response is not currepted by server, do it in loop + foreach (XmlRpcStructMember MEMBER in mainStruct.Members) + { + switch (MEMBER.Name) + { + case "status": R.Status = (string)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "seconds": R.Seconds = (double)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "data": + XmlRpcValueStruct dataStruct = (XmlRpcValueStruct)MEMBER.Data; + foreach (XmlRpcStructMember dataMemeber in dataStruct.Members) + { + OSHConsole.WriteLine(" >" + dataMemeber.Name + "= " + dataMemeber.Data.Data.ToString()); + switch (dataMemeber.Name) + { + case "SubRating": R.SubRating = dataMemeber.Data.Data.ToString(); break; + case "SubSumVotes": R.SubSumVotes = dataMemeber.Data.Data.ToString(); break; + case "IDSubtitle": R.IDSubtitle = dataMemeber.Data.Data.ToString(); break; + } + } + break; + } + } + // Return the response to user !! + return R; + } + } + } + else + { + OSHConsole.WriteLine(response, DebugCode.Error); + return new MethodResponseError("Fail", response); + } + return new MethodResponseError("Fail", "SubtitlesVote call failed !"); + } + /// + /// Add comment to a subtitle + /// + /// Subtitle identifier (BEWARE! this is not the ID of subtitle file but of the whole subtitle (a subtitle can contain multiple subtitle files)) + /// User's comment + /// Optional parameter. If set to 1, subtitles are marked as bad. + /// Status of the call operation. If the call success the response will be 'MethodResponseAddComment' + public static IMethodResponse AddComment(int idsubtitle, string comment, int badsubtitle) + { + if (TOKEN == "") + { + OSHConsole.WriteLine("Can't do this call, 'token' value not set. Please use Log In method first.", DebugCode.Error); + return new MethodResponseError("Fail", "Can't do this call, 'token' value not set. Please use Log In method first."); + } + // Method call .. + List parms = new List(); + parms.Add(new XmlRpcValueBasic(TOKEN, XmlRpcBasicValueType.String)); + XmlRpcValueStruct s = new XmlRpcValueStruct(new List()); + s.Members.Add(new XmlRpcStructMember("idsubtitle", new XmlRpcValueBasic(idsubtitle))); + s.Members.Add(new XmlRpcStructMember("comment", new XmlRpcValueBasic(comment))); + s.Members.Add(new XmlRpcStructMember("badsubtitle", new XmlRpcValueBasic(badsubtitle))); + parms.Add(s); + XmlRpcMethodCall call = new XmlRpcMethodCall("AddComment", parms); + + OSHConsole.WriteLine("Sending AddComment request to the server ...", DebugCode.Good); + // Send the request to the server + string response = Utilities.GetStreamString(Utilities.SendRequest(XmlRpcGenerator.Generate(call), XML_PRC_USERAGENT)); + if (!response.Contains("ERROR:")) + { + // No error occur, get and decode the response. + XmlRpcMethodCall[] calls = XmlRpcGenerator.DecodeMethodResponse(response); + if (calls.Length > 0) + { + if (calls[0].Parameters.Count > 0) + { + XmlRpcValueStruct mainStruct = (XmlRpcValueStruct)calls[0].Parameters[0]; + // Create the response, we'll need it later + MethodResponseAddComment R = new MethodResponseAddComment(); + + // To make sure response is not currepted by server, do it in loop + foreach (XmlRpcStructMember MEMBER in mainStruct.Members) + { + switch (MEMBER.Name) + { + case "status": R.Status = (string)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "seconds": R.Seconds = (double)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + } + } + // Return the response to user !! + return R; + } + } + } + else + { + OSHConsole.WriteLine(response, DebugCode.Error); + return new MethodResponseError("Fail", response); + } + return new MethodResponseError("Fail", "AddComment call failed !"); + } + /// + /// Add new request for subtitles, user must be logged in + /// + /// The subtitle language id 3 length + /// http://www.imdb.com/ + /// The comment + /// Status of the call operation. If the call success the response will be 'MethodResponseAddRequest' + public static IMethodResponse AddRequest(string sublanguageid, string idmovieimdb, string comment) + { + if (TOKEN == "") + { + OSHConsole.WriteLine("Can't do this call, 'token' value not set. Please use Log In method first.", DebugCode.Error); + return new MethodResponseError("Fail", "Can't do this call, 'token' value not set. Please use Log In method first."); + } + // Method call .. + List parms = new List(); + parms.Add(new XmlRpcValueBasic(TOKEN, XmlRpcBasicValueType.String)); + XmlRpcValueStruct s = new XmlRpcValueStruct(new List()); + s.Members.Add(new XmlRpcStructMember("sublanguageid", new XmlRpcValueBasic(sublanguageid))); + s.Members.Add(new XmlRpcStructMember("idmovieimdb", new XmlRpcValueBasic(idmovieimdb))); + s.Members.Add(new XmlRpcStructMember("comment", new XmlRpcValueBasic(comment))); + parms.Add(s); + XmlRpcMethodCall call = new XmlRpcMethodCall("AddRequest", parms); + + OSHConsole.WriteLine("Sending AddRequest request to the server ...", DebugCode.Good); + // Send the request to the server + string response = Utilities.GetStreamString(Utilities.SendRequest(XmlRpcGenerator.Generate(call), XML_PRC_USERAGENT)); + if (!response.Contains("ERROR:")) + { + // No error occur, get and decode the response. + XmlRpcMethodCall[] calls = XmlRpcGenerator.DecodeMethodResponse(response); + if (calls.Length > 0) + { + if (calls[0].Parameters.Count > 0) + { + XmlRpcValueStruct mainStruct = (XmlRpcValueStruct)calls[0].Parameters[0]; + // Create the response, we'll need it later + MethodResponseAddRequest R = new MethodResponseAddRequest(); + + // To make sure response is not currepted by server, do it in loop + foreach (XmlRpcStructMember MEMBER in mainStruct.Members) + { + switch (MEMBER.Name) + { + case "status": R.Status = (string)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "seconds": R.Seconds = (double)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "data": + XmlRpcValueStruct dataStruct = (XmlRpcValueStruct)MEMBER.Data; + foreach (XmlRpcStructMember dataMemeber in dataStruct.Members) + { + switch (dataMemeber.Name) + { + case "request_url": R.request_url = dataMemeber.Data.Data.ToString(); OSHConsole.WriteLine(">" + dataMemeber.Name + "= " + dataMemeber.Data.Data.ToString()); break; + } + } + break; + } + } + // Return the response to user !! + return R; + } + } + } + else + { + OSHConsole.WriteLine(response, DebugCode.Error); + return new MethodResponseError("Fail", response); + } + return new MethodResponseError("Fail", "AddRequest call failed !"); + } + /*User interface*/ + /// + /// Get list of supported subtitle languages + /// + /// ISO639-1 2-letter language code of user's interface language. + /// Status of the call operation. If the call success the response will be 'MethodResponseGetSubLanguages' + public static IMethodResponse GetSubLanguages(string language) + { + if (TOKEN == "") + { + OSHConsole.WriteLine("Can't do this call, 'token' value not set. Please use Log In method first.", DebugCode.Error); + return new MethodResponseError("Fail", "Can't do this call, 'token' value not set. Please use Log In method first."); + } + // Method call .. + List parms = new List(); + parms.Add(new XmlRpcValueBasic(TOKEN)); + parms.Add(new XmlRpcValueBasic(language)); + XmlRpcMethodCall call = new XmlRpcMethodCall("GetSubLanguages", parms); + + OSHConsole.WriteLine("Sending GetSubLanguages request to the server ...", DebugCode.Good); + // Send the request to the server + string response = Utilities.GetStreamString(Utilities.SendRequest(XmlRpcGenerator.Generate(call), XML_PRC_USERAGENT)); + if (!response.Contains("ERROR:")) + { + // No error occur, get and decode the response. + XmlRpcMethodCall[] calls = XmlRpcGenerator.DecodeMethodResponse(response); + if (calls.Length > 0) + { + if (calls[0].Parameters.Count > 0) + { + XmlRpcValueStruct mainStruct = (XmlRpcValueStruct)calls[0].Parameters[0]; + // Create the response, we'll need it later + MethodResponseGetSubLanguages R = new MethodResponseGetSubLanguages(); + + // To make sure response is not currepted by server, do it in loop + foreach (XmlRpcStructMember MEMBER in mainStruct.Members) + { + switch (MEMBER.Name) + { + case "status": R.Status = (string)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "seconds": R.Seconds = (double)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "data":// array of structs + XmlRpcValueArray array = (XmlRpcValueArray)MEMBER.Data; + foreach (IXmlRpcValue value in array.Values) + { + if (value is XmlRpcValueStruct) + { + XmlRpcValueStruct valueStruct = (XmlRpcValueStruct)value; + SubtitleLanguage lang = new SubtitleLanguage(); + OSHConsole.WriteLine(">SubLanguage:"); + foreach (XmlRpcStructMember langMemeber in valueStruct.Members) + { + OSHConsole.WriteLine(" >" + langMemeber.Name + "= " + langMemeber.Data.Data.ToString()); + switch (langMemeber.Name) + { + case "SubLanguageID": lang.SubLanguageID = langMemeber.Data.Data.ToString(); break; + case "LanguageName": lang.LanguageName = langMemeber.Data.Data.ToString(); break; + case "ISO639": lang.ISO639 = langMemeber.Data.Data.ToString(); break; + } + } + R.Languages.Add(lang); + } + else + { + OSHConsole.WriteLine(">" + MEMBER.Name + "= " + + MEMBER.Data.Data.ToString() + " [Struct expected !]", DebugCode.Warning); + } + } + break; + } + } + // Return the response to user !! + return R; + } + } + } + else + { + OSHConsole.WriteLine(response, DebugCode.Error); + return new MethodResponseError("Fail", response); + } + return new MethodResponseError("Fail", "GetSubLanguages call failed !"); + } + /// + /// Detect language for given strings + /// + /// Array of strings you want language detected for + /// The encoding that will be used to get buffer of given strings. (this is not OS official parameter) + /// Status of the call operation. If the call success the response will be 'MethodResponseDetectLanguage' + public static IMethodResponse DetectLanguage(string[] texts, Encoding encodingUsed) + { + if (TOKEN == "") + { + OSHConsole.WriteLine("Can't do this call, 'token' value not set. Please use Log In method first.", DebugCode.Error); + return new MethodResponseError("Fail", "Can't do this call, 'token' value not set. Please use Log In method first."); + } + // Method call .. + List parms = new List(); + parms.Add(new XmlRpcValueBasic(TOKEN, XmlRpcBasicValueType.String)); + // We need to gzip texts then code them with base 24 + List decodedTexts = new List(); + foreach (string text in texts) + { + // compress + Stream str = new MemoryStream(); + byte[] stringData = encodingUsed.GetBytes(text); + str.Write(stringData, 0, stringData.Length); + str.Position = 0; + byte[] data = Utilities.Compress(str); + //base 64 + decodedTexts.Add(Convert.ToBase64String(data)); + } + parms.Add(new XmlRpcValueArray(decodedTexts.ToArray())); + XmlRpcMethodCall call = new XmlRpcMethodCall("DetectLanguage", parms); + + OSHConsole.WriteLine("Sending DetectLanguage request to the server ...", DebugCode.Good); + // Send the request to the server + string response = Utilities.GetStreamString(Utilities.SendRequest(XmlRpcGenerator.Generate(call), XML_PRC_USERAGENT)); + if (!response.Contains("ERROR:")) + { + // No error occur, get and decode the response. + XmlRpcMethodCall[] calls = XmlRpcGenerator.DecodeMethodResponse(response); + if (calls.Length > 0) + { + if (calls[0].Parameters.Count > 0) + { + XmlRpcValueStruct mainStruct = (XmlRpcValueStruct)calls[0].Parameters[0]; + // Create the response, we'll need it later + MethodResponseDetectLanguage R = new MethodResponseDetectLanguage(); + + // To make sure response is not currepted by server, do it in loop + foreach (XmlRpcStructMember MEMBER in mainStruct.Members) + { + switch (MEMBER.Name) + { + case "status": R.Status = (string)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "seconds": R.Seconds = (double)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "data": + if (MEMBER.Data is XmlRpcValueStruct) + { + OSHConsole.WriteLine(">Languages:"); + XmlRpcValueStruct dataStruct = (XmlRpcValueStruct)MEMBER.Data; + foreach (XmlRpcStructMember dataMember in dataStruct.Members) + { + DetectLanguageResult lang = new DetectLanguageResult(); + lang.InputSample = dataMember.Name; + lang.LanguageID = dataMember.Data.Data.ToString(); + R.Results.Add(lang); + OSHConsole.WriteLine(" >" + dataMember.Name + " (" + dataMember.Data.Data.ToString() + ")"); + } + } + else + { + OSHConsole.WriteLine(">Languages ?? Struct expected but server return another type!!", DebugCode.Warning); + } + break; + } + } + // Return the response to user !! + return R; + } + } + } + else + { + OSHConsole.WriteLine(response, DebugCode.Error); + return new MethodResponseError("Fail", response); + } + return new MethodResponseError("Fail", "DetectLanguage call failed !"); + } + /// + /// Get available translations for given program + /// + /// Name of the program/client application you want translations for. Currently supported values: subdownloader, oscar + /// Status of the call operation. If the call success the response will be 'MethodResponseGetAvailableTranslations' + public static IMethodResponse GetAvailableTranslations(string program) + { + if (TOKEN == "") + { + OSHConsole.WriteLine("Can't do this call, 'token' value not set. Please use Log In method first.", DebugCode.Error); + return new MethodResponseError("Fail", "Can't do this call, 'token' value not set. Please use Log In method first."); + } + // Method call .. + List parms = new List(); + parms.Add(new XmlRpcValueBasic(TOKEN)); + parms.Add(new XmlRpcValueBasic(program)); + XmlRpcMethodCall call = new XmlRpcMethodCall("GetAvailableTranslations", parms); + + OSHConsole.WriteLine("Sending GetAvailableTranslations request to the server ...", DebugCode.Good); + // Send the request to the server + string response = Utilities.GetStreamString(Utilities.SendRequest(XmlRpcGenerator.Generate(call), XML_PRC_USERAGENT)); + if (!response.Contains("ERROR:")) + { + // No error occur, get and decode the response. + XmlRpcMethodCall[] calls = XmlRpcGenerator.DecodeMethodResponse(response); + if (calls.Length > 0) + { + if (calls[0].Parameters.Count > 0) + { + XmlRpcValueStruct mainStruct = (XmlRpcValueStruct)calls[0].Parameters[0]; + // Create the response, we'll need it later + MethodResponseGetAvailableTranslations R = new MethodResponseGetAvailableTranslations(); + + // To make sure response is not currepted by server, do it in loop + foreach (XmlRpcStructMember MEMBER in mainStruct.Members) + { + switch (MEMBER.Name) + { + case "status": R.Status = (string)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "seconds": R.Seconds = (double)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "data": + XmlRpcValueStruct dataStruct = (XmlRpcValueStruct)MEMBER.Data; + OSHConsole.WriteLine(">data:"); + foreach (XmlRpcStructMember dataMember in dataStruct.Members) + { + if (dataMember.Data is XmlRpcValueStruct) + { + XmlRpcValueStruct resStruct = (XmlRpcValueStruct)dataMember.Data; + GetAvailableTranslationsResult res = new GetAvailableTranslationsResult(); + res.LanguageID = dataMember.Name; + OSHConsole.WriteLine(" >LanguageID: " + dataMember.Name); + foreach (XmlRpcStructMember resMember in resStruct.Members) + { + switch (resMember.Name) + { + case "LastCreated": res.LastCreated = resMember.Data.Data.ToString(); OSHConsole.WriteLine(" >" + resMember.Name + "= " + resMember.Data.Data.ToString()); break; + case "StringsNo": res.StringsNo = resMember.Data.Data.ToString(); OSHConsole.WriteLine(" >" + resMember.Name + "= " + resMember.Data.Data.ToString()); break; + } + R.Results.Add(res); + } + } + else + { + OSHConsole.WriteLine(" >Struct expected !!", DebugCode.Warning); + } + } + break; + } + } + // Return the response to user !! + return R; + } + } + } + else + { + OSHConsole.WriteLine(response, DebugCode.Error); + return new MethodResponseError("Fail", response); + } + return new MethodResponseError("Fail", "GetAvailableTranslations call failed !"); + } + /// + /// Get a translation for given program and language + /// + /// language ​ISO639-1 2-letter code + /// available formats: [gnugettext compatible: mo, po] and [additional: txt, xml] + /// Name of the program/client application you want translations for. (currently supported values: subdownloader, oscar) + /// Status of the call operation. If the call success the response will be 'MethodResponseGetTranslation' + public static IMethodResponse GetTranslation(string iso639, string format, string program) + { + if (TOKEN == "") + { + OSHConsole.WriteLine("Can't do this call, 'token' value not set. Please use Log In method first.", DebugCode.Error); + return new MethodResponseError("Fail", "Can't do this call, 'token' value not set. Please use Log In method first."); + } + // Method call .. + List parms = new List(); + parms.Add(new XmlRpcValueBasic(TOKEN)); + parms.Add(new XmlRpcValueBasic(iso639)); + parms.Add(new XmlRpcValueBasic(format)); + parms.Add(new XmlRpcValueBasic(program)); + XmlRpcMethodCall call = new XmlRpcMethodCall("GetTranslation", parms); + + OSHConsole.WriteLine("Sending GetTranslation request to the server ...", DebugCode.Good); + // Send the request to the server + //File.WriteAllText(".\\REQUEST_GetTranslation.xml", Encoding.ASCII.GetString(XmlRpcGenerator.Generate(call))); + string response = Utilities.GetStreamString(Utilities.SendRequest(XmlRpcGenerator.Generate(call), XML_PRC_USERAGENT)); + if (!response.Contains("ERROR:")) + { + // No error occur, get and decode the response. + XmlRpcMethodCall[] calls = XmlRpcGenerator.DecodeMethodResponse(response); + if (calls.Length > 0) + { + if (calls[0].Parameters.Count > 0) + { + XmlRpcValueStruct mainStruct = (XmlRpcValueStruct)calls[0].Parameters[0]; + // Create the response, we'll need it later + MethodResponseGetTranslation R = new MethodResponseGetTranslation(); + + // To make sure response is not currepted by server, do it in loop + foreach (XmlRpcStructMember MEMBER in mainStruct.Members) + { + switch (MEMBER.Name) + { + case "status": R.Status = (string)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "seconds": R.Seconds = (double)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "data": R.ContentData = (string)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + } + } + // Return the response to user !! + return R; + } + } + } + else + { + OSHConsole.WriteLine(response, DebugCode.Error); + return new MethodResponseError("Fail", response); + } + return new MethodResponseError("Fail", "GetTranslation call failed !"); + } + /// + /// Check for the latest version of given application + /// + /// name of the program/client application you want to check. (Currently supported values: subdownloader, oscar) + /// Status of the call operation. If the call success the response will be 'MethodResponseAutoUpdate' + public static IMethodResponse AutoUpdate(string program) + { + /*if (TOKEN == "") + { + OSHConsole.WriteLine("Can't do this call, 'token' value not set. Please use Log In method first.", DebugCode.Error); + return new MethodResponseError("Fail", "Can't do this call, 'token' value not set. Please use Log In method first."); + }*/ + // Method call .. + List parms = new List(); + // parms.Add(new XmlRpcValueBasic(TOKEN)); + parms.Add(new XmlRpcValueBasic(program)); + // parms.Add(new XmlRpcValueBasic(XML_PRC_USERAGENT)); + + XmlRpcMethodCall call = new XmlRpcMethodCall("AutoUpdate", parms); + OSHConsole.WriteLine("Sending AutoUpdate request to the server ...", DebugCode.Good); + // Send the request to the server + string response = Utilities.GetStreamString(Utilities.SendRequest(XmlRpcGenerator.Generate(call), XML_PRC_USERAGENT)); + if (!response.Contains("ERROR:")) + { + // No error occur, get and decode the response. + XmlRpcMethodCall[] calls = XmlRpcGenerator.DecodeMethodResponse(response); + if (calls.Length > 0) + { + if (calls[0].Parameters.Count > 0) + { + XmlRpcValueStruct mainStruct = (XmlRpcValueStruct)calls[0].Parameters[0]; + // Create the response, we'll need it later + MethodResponseAutoUpdate R = new MethodResponseAutoUpdate(); + + // To make sure response is not currepted by server, do it in loop + foreach (XmlRpcStructMember MEMBER in mainStruct.Members) + { + switch (MEMBER.Name) + { + case "status": R.Status = (string)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "seconds": R.Seconds = (double)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "version": R.version = (string)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "url_windows": R.url_windows = (string)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "url_linux": R.url_linux = (string)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "comments": R.comments = (string)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + } + } + // Return the response to user !! + return R; + } + } + } + else + { + OSHConsole.WriteLine(response, DebugCode.Error); + return new MethodResponseError("Fail", response); + } + return new MethodResponseError("Fail", "AutoUpdate call failed !"); + } + /*Checking*/ + /// + /// Check if video file hashes are already stored in the database + /// + /// Array of video file hashes + /// Status of the call operation. If the call success the response will be 'MethodResponseCheckMovieHash' + public static IMethodResponse CheckMovieHash(string[] hashes) + { + if (TOKEN == "") + { + OSHConsole.WriteLine("Can't do this call, 'token' value not set. Please use Log In method first.", DebugCode.Error); + return new MethodResponseError("Fail", "Can't do this call, 'token' value not set. Please use Log In method first."); + } + // Method call .. + List parms = new List(); + parms.Add(new XmlRpcValueBasic(TOKEN)); + parms.Add(new XmlRpcValueArray(hashes)); + XmlRpcMethodCall call = new XmlRpcMethodCall("CheckMovieHash", parms); + + OSHConsole.WriteLine("Sending CheckMovieHash request to the server ...", DebugCode.Good); + // Send the request to the server + string response = Utilities.GetStreamString(Utilities.SendRequest(XmlRpcGenerator.Generate(call), XML_PRC_USERAGENT)); + if (!response.Contains("ERROR:")) + { + // No error occur, get and decode the response. + XmlRpcMethodCall[] calls = XmlRpcGenerator.DecodeMethodResponse(response); + if (calls.Length > 0) + { + if (calls[0].Parameters.Count > 0) + { + XmlRpcValueStruct mainStruct = (XmlRpcValueStruct)calls[0].Parameters[0]; + // Create the response, we'll need it later + MethodResponseCheckMovieHash R = new MethodResponseCheckMovieHash(); + + // To make sure response is not currepted by server, do it in loop + foreach (XmlRpcStructMember MEMBER in mainStruct.Members) + { + switch (MEMBER.Name) + { + case "status": R.Status = (string)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "seconds": R.Seconds = (double)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "data": + XmlRpcValueStruct dataStruct = (XmlRpcValueStruct)MEMBER.Data; + OSHConsole.WriteLine(">Data:"); + foreach (XmlRpcStructMember dataMember in dataStruct.Members) + { + CheckMovieHashResult res = new CheckMovieHashResult(); + res.Name = dataMember.Name; + OSHConsole.WriteLine(" >" + res.Name + ":"); + XmlRpcValueStruct movieStruct = (XmlRpcValueStruct)dataMember.Data; + foreach (XmlRpcStructMember movieMember in movieStruct.Members) + { + switch (movieMember.Name) + { + case "MovieHash": res.MovieHash = movieMember.Data.Data.ToString(); OSHConsole.WriteLine(" >" + movieMember.Name + "= " + movieMember.Data.Data.ToString()); break; + case "MovieImdbID": res.MovieImdbID = movieMember.Data.Data.ToString(); OSHConsole.WriteLine(" >" + movieMember.Name + "= " + movieMember.Data.Data.ToString()); break; + case "MovieName": res.MovieName = movieMember.Data.Data.ToString(); OSHConsole.WriteLine(" >" + movieMember.Name + "= " + movieMember.Data.Data.ToString()); break; + case "MovieYear": res.MovieYear = movieMember.Data.Data.ToString(); OSHConsole.WriteLine(" >" + movieMember.Name + "= " + movieMember.Data.Data.ToString()); break; + } + } + R.Results.Add(res); + } + break; + } + } + // Return the response to user !! + return R; + } + } + } + else + { + OSHConsole.WriteLine(response, DebugCode.Error); + return new MethodResponseError("Fail", response); + } + return new MethodResponseError("Fail", "CheckMovieHash call failed !"); + } + /// + /// Check if video file hashes are already stored in the database. This method returns matching !MovieImdbID, MovieName, MovieYear, SeriesSeason, SeriesEpisode, + /// MovieKind if available for each $moviehash, always sorted by SeenCount DESC. + /// + /// Array of video file hashes + /// Status of the call operation. If the call success the response will be 'MethodResponseCheckMovieHash2' + public static IMethodResponse CheckMovieHash2(string[] hashes) + { + if (TOKEN == "") + { + OSHConsole.WriteLine("Can't do this call, 'token' value not set. Please use Log In method first.", DebugCode.Error); + return new MethodResponseError("Fail", "Can't do this call, 'token' value not set. Please use Log In method first."); + } + // Method call .. + List parms = new List(); + parms.Add(new XmlRpcValueBasic(TOKEN)); + parms.Add(new XmlRpcValueArray(hashes)); + XmlRpcMethodCall call = new XmlRpcMethodCall("CheckMovieHash2", parms); + + OSHConsole.WriteLine("Sending CheckMovieHash2 request to the server ...", DebugCode.Good); + // Send the request to the server + string response = Utilities.GetStreamString(Utilities.SendRequest(XmlRpcGenerator.Generate(call), XML_PRC_USERAGENT)); + if (!response.Contains("ERROR:")) + { + // No error occur, get and decode the response. + XmlRpcMethodCall[] calls = XmlRpcGenerator.DecodeMethodResponse(response); + if (calls.Length > 0) + { + if (calls[0].Parameters.Count > 0) + { + XmlRpcValueStruct mainStruct = (XmlRpcValueStruct)calls[0].Parameters[0]; + // Create the response, we'll need it later + MethodResponseCheckMovieHash2 R = new MethodResponseCheckMovieHash2(); + + // To make sure response is not currepted by server, do it in loop + foreach (XmlRpcStructMember MEMBER in mainStruct.Members) + { + switch (MEMBER.Name) + { + case "status": R.Status = (string)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "seconds": R.Seconds = (double)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "data": + XmlRpcValueStruct dataStruct = (XmlRpcValueStruct)MEMBER.Data; + OSHConsole.WriteLine(">Data:"); + foreach (XmlRpcStructMember dataMember in dataStruct.Members) + { + CheckMovieHash2Result res = new CheckMovieHash2Result(); + res.Name = dataMember.Name; + OSHConsole.WriteLine(" >" + res.Name + ":"); + + XmlRpcValueArray dataArray = (XmlRpcValueArray)dataMember.Data; + foreach (XmlRpcValueStruct movieStruct in dataArray.Values) + { + CheckMovieHash2Data d = new CheckMovieHash2Data(); + foreach (XmlRpcStructMember movieMember in movieStruct.Members) + { + switch (movieMember.Name) + { + case "MovieHash": d.MovieHash = movieMember.Data.Data.ToString(); OSHConsole.WriteLine(" >" + movieMember.Name + "= " + movieMember.Data.Data.ToString()); break; + case "MovieImdbID": d.MovieImdbID = movieMember.Data.Data.ToString(); OSHConsole.WriteLine(" >" + movieMember.Name + "= " + movieMember.Data.Data.ToString()); break; + case "MovieName": d.MovieName = movieMember.Data.Data.ToString(); OSHConsole.WriteLine(" >" + movieMember.Name + "= " + movieMember.Data.Data.ToString()); break; + case "MovieYear": d.MovieYear = movieMember.Data.Data.ToString(); OSHConsole.WriteLine(" >" + movieMember.Name + "= " + movieMember.Data.Data.ToString()); break; + case "MovieKind": d.MovieKind = movieMember.Data.Data.ToString(); OSHConsole.WriteLine(" >" + movieMember.Name + "= " + movieMember.Data.Data.ToString()); break; + case "SeriesSeason": d.SeriesSeason = movieMember.Data.Data.ToString(); OSHConsole.WriteLine(" >" + movieMember.Name + "= " + movieMember.Data.Data.ToString()); break; + case "SeriesEpisode": d.SeriesEpisode = movieMember.Data.Data.ToString(); OSHConsole.WriteLine(" >" + movieMember.Name + "= " + movieMember.Data.Data.ToString()); break; + case "SeenCount": d.MovieYear = movieMember.Data.Data.ToString(); OSHConsole.WriteLine(" >" + movieMember.Name + "= " + movieMember.Data.Data.ToString()); break; + } + } + res.Items.Add(d); + } + R.Results.Add(res); + } + break; + } + } + // Return the response to user !! + return R; + } + } + } + else + { + OSHConsole.WriteLine(response, DebugCode.Error); + return new MethodResponseError("Fail", response); + } + return new MethodResponseError("Fail", "CheckMovieHash2 call failed !"); + } + /// + /// Check if given subtitle files are already stored in the database + /// + /// Array of subtitle file hashes (MD5 hashes of subtitle file contents) + /// Status of the call operation. If the call success the response will be 'MethodResponseCheckSubHash' + public static IMethodResponse CheckSubHash(string[] hashes) + { + if (TOKEN == "") + { + OSHConsole.WriteLine("Can't do this call, 'token' value not set. Please use Log In method first.", DebugCode.Error); + return new MethodResponseError("Fail", "Can't do this call, 'token' value not set. Please use Log In method first."); + } + // Method call .. + List parms = new List(); + parms.Add(new XmlRpcValueBasic(TOKEN)); + parms.Add(new XmlRpcValueArray(hashes)); + XmlRpcMethodCall call = new XmlRpcMethodCall("CheckSubHash", parms); + + OSHConsole.WriteLine("Sending CheckSubHash request to the server ...", DebugCode.Good); + // Send the request to the server + string response = Utilities.GetStreamString(Utilities.SendRequest(XmlRpcGenerator.Generate(call), XML_PRC_USERAGENT)); + if (!response.Contains("ERROR:")) + { + // No error occur, get and decode the response. + XmlRpcMethodCall[] calls = XmlRpcGenerator.DecodeMethodResponse(response); + if (calls.Length > 0) + { + if (calls[0].Parameters.Count > 0) + { + XmlRpcValueStruct mainStruct = (XmlRpcValueStruct)calls[0].Parameters[0]; + // Create the response, we'll need it later + MethodResponseCheckSubHash R = new MethodResponseCheckSubHash(); + + // To make sure response is not currepted by server, do it in loop + foreach (XmlRpcStructMember MEMBER in mainStruct.Members) + { + switch (MEMBER.Name) + { + case "status": R.Status = (string)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "seconds": R.Seconds = (double)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "data": + OSHConsole.WriteLine(">Data:"); + XmlRpcValueStruct dataStruct = (XmlRpcValueStruct)MEMBER.Data; + foreach (XmlRpcStructMember dataMember in dataStruct.Members) + { + OSHConsole.WriteLine(" >" + dataMember.Name + "= " + dataMember.Data.Data.ToString()); + CheckSubHashResult r = new CheckSubHashResult(); + r.Hash = dataMember.Name; + r.SubID = dataMember.Data.Data.ToString(); + R.Results.Add(r); + } + break; + } + } + // Return the response to user !! + return R; + } + } + } + else + { + OSHConsole.WriteLine(response, DebugCode.Error); + return new MethodResponseError("Fail", response); + } + return new MethodResponseError("Fail", "CheckSubHash call failed !"); + } + /*Upload*/ + /// + /// Try to upload subtitles, perform pre-upload checking (i.e. check if subtitles already exist on server) + /// + /// The subtitle parameters collection to try to upload + /// Status of the call operation. If the call success the response will be 'MethodResponseTryUploadSubtitles' + public static IMethodResponse TryUploadSubtitles(TryUploadSubtitlesParameters[] subs) + { + if (TOKEN == "") + { + OSHConsole.WriteLine("Can't do this call, 'token' value not set. Please use Log In method first.", DebugCode.Error); + return new MethodResponseError("Fail", "Can't do this call, 'token' value not set. Please use Log In method first."); + } + // Method call .. + List parms = new List(); + parms.Add(new XmlRpcValueBasic(TOKEN, XmlRpcBasicValueType.String)); + XmlRpcValueStruct s = new XmlRpcValueStruct(new List()); + int i = 1; + foreach (TryUploadSubtitlesParameters cd in subs) + { + XmlRpcStructMember member = new XmlRpcStructMember("cd" + i, null); + XmlRpcValueStruct memberStruct = new XmlRpcValueStruct(new List()); + memberStruct.Members.Add(new XmlRpcStructMember("subhash", new XmlRpcValueBasic(cd.subhash))); + memberStruct.Members.Add(new XmlRpcStructMember("subfilename", new XmlRpcValueBasic(cd.subfilename))); + memberStruct.Members.Add(new XmlRpcStructMember("moviehash", new XmlRpcValueBasic(cd.moviehash))); + memberStruct.Members.Add(new XmlRpcStructMember("moviebytesize", new XmlRpcValueBasic(cd.moviebytesize))); + memberStruct.Members.Add(new XmlRpcStructMember("moviefps", new XmlRpcValueBasic(cd.moviefps))); + memberStruct.Members.Add(new XmlRpcStructMember("movietimems", new XmlRpcValueBasic(cd.movietimems))); + memberStruct.Members.Add(new XmlRpcStructMember("movieframes", new XmlRpcValueBasic(cd.movieframes))); + memberStruct.Members.Add(new XmlRpcStructMember("moviefilename", new XmlRpcValueBasic(cd.moviefilename))); + member.Data = memberStruct; + s.Members.Add(member); + i++; + } + parms.Add(s); + XmlRpcMethodCall call = new XmlRpcMethodCall("TryUploadSubtitles", parms); + + OSHConsole.WriteLine("Sending TryUploadSubtitles request to the server ...", DebugCode.Good); + // Send the request to the server + string response = Utilities.GetStreamString(Utilities.SendRequest(XmlRpcGenerator.Generate(call), XML_PRC_USERAGENT)); + if (!response.Contains("ERROR:")) + { + // No error occur, get and decode the response. + XmlRpcMethodCall[] calls = XmlRpcGenerator.DecodeMethodResponse(response); + if (calls.Length > 0) + { + if (calls[0].Parameters.Count > 0) + { + XmlRpcValueStruct mainStruct = (XmlRpcValueStruct)calls[0].Parameters[0]; + // Create the response, we'll need it later + MethodResponseTryUploadSubtitles R = new MethodResponseTryUploadSubtitles(); + + // To make sure response is not currepted by server, do it in loop + foreach (XmlRpcStructMember MEMBER in mainStruct.Members) + { + switch (MEMBER.Name) + { + case "status": R.Status = (string)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "seconds": R.Seconds = (double)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "alreadyindb": R.AlreadyInDB = (int)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "data": + if (MEMBER.Data is XmlRpcValueArray) + { + OSHConsole.WriteLine("Results: "); + + XmlRpcValueArray rarray = (XmlRpcValueArray)MEMBER.Data; + foreach (IXmlRpcValue subStruct in rarray.Values) + { + if (subStruct == null) continue; + if (!(subStruct is XmlRpcValueStruct)) continue; + + SubtitleSearchResult result = new SubtitleSearchResult(); + foreach (XmlRpcStructMember submember in ((XmlRpcValueStruct)subStruct).Members) + { + // To avoid errors of arranged info or missing ones, let's do it with switch.. + switch (submember.Name) + { + case "IDMovie": result.IDMovie = submember.Data.Data.ToString(); break; + case "IDMovieImdb": result.IDMovieImdb = submember.Data.Data.ToString(); break; + case "IDSubMovieFile": result.IDSubMovieFile = submember.Data.Data.ToString(); break; + case "IDSubtitle": result.IDSubtitle = submember.Data.Data.ToString(); break; + case "IDSubtitleFile": result.IDSubtitleFile = submember.Data.Data.ToString(); break; + case "ISO639": result.ISO639 = submember.Data.Data.ToString(); break; + case "LanguageName": result.LanguageName = submember.Data.Data.ToString(); break; + case "MovieByteSize": result.MovieByteSize = submember.Data.Data.ToString(); break; + case "MovieHash": result.MovieHash = submember.Data.Data.ToString(); break; + case "MovieImdbRating": result.MovieImdbRating = submember.Data.Data.ToString(); break; + case "MovieName": result.MovieName = submember.Data.Data.ToString(); break; + case "MovieNameEng": result.MovieNameEng = submember.Data.Data.ToString(); break; + case "MovieReleaseName": result.MovieReleaseName = submember.Data.Data.ToString(); break; + case "MovieTimeMS": result.MovieTimeMS = submember.Data.Data.ToString(); break; + case "MovieYear": result.MovieYear = submember.Data.Data.ToString(); break; + case "SubActualCD": result.SubActualCD = submember.Data.Data.ToString(); break; + case "SubAddDate": result.SubAddDate = submember.Data.Data.ToString(); break; + case "SubAuthorComment": result.SubAuthorComment = submember.Data.Data.ToString(); break; + case "SubBad": result.SubBad = submember.Data.Data.ToString(); break; + case "SubDownloadLink": result.SubDownloadLink = submember.Data.Data.ToString(); break; + case "SubDownloadsCnt": result.SubDownloadsCnt = submember.Data.Data.ToString(); break; + case "SubFileName": result.SubFileName = submember.Data.Data.ToString(); break; + case "SubFormat": result.SubFormat = submember.Data.Data.ToString(); break; + case "SubHash": result.SubHash = submember.Data.Data.ToString(); break; + case "SubLanguageID": result.SubLanguageID = submember.Data.Data.ToString(); break; + case "SubRating": result.SubRating = submember.Data.Data.ToString(); break; + case "SubSize": result.SubSize = submember.Data.Data.ToString(); break; + case "SubSumCD": result.SubSumCD = submember.Data.Data.ToString(); break; + case "UserID": result.UserID = submember.Data.Data.ToString(); break; + case "UserNickName": result.UserNickName = submember.Data.Data.ToString(); break; + case "ZipDownloadLink": result.ZipDownloadLink = submember.Data.Data.ToString(); break; + } + } + R.Results.Add(result); + OSHConsole.WriteLine(">" + result.ToString()); + } + } + else// Unknown data ? + { + OSHConsole.WriteLine("Data= " + MEMBER.Data.Data.ToString(), DebugCode.Warning); + } + break; + } + } + // Return the response to user !! + return R; + } + } + } + else + { + OSHConsole.WriteLine(response, DebugCode.Error); + return new MethodResponseError("Fail", response); + } + return new MethodResponseError("Fail", "TryUploadSubtitles call failed !"); + } + /// + /// Upload given subtitles to OSDb server + /// + /// The pamaters of upload method + /// Status of the call operation. If the call success the response will be 'MethodResponseUploadSubtitles' + public static IMethodResponse UploadSubtitles(UploadSubtitleInfoParameters info) + { + if (TOKEN == "") + { + OSHConsole.WriteLine("Can't do this call, 'token' value not set. Please use Log In method first.", DebugCode.Error); + return new MethodResponseError("Fail", "Can't do this call, 'token' value not set. Please use Log In method first."); + } + // Method call .. + List parms = new List(); + parms.Add(new XmlRpcValueBasic(TOKEN)); + // Main struct + XmlRpcValueStruct s = new XmlRpcValueStruct(new List()); + + // Base info member as struct + XmlRpcStructMember member = new XmlRpcStructMember("baseinfo", null); + XmlRpcValueStruct memberStruct = new XmlRpcValueStruct(new List()); + memberStruct.Members.Add(new XmlRpcStructMember("idmovieimdb", new XmlRpcValueBasic(info.idmovieimdb))); + memberStruct.Members.Add(new XmlRpcStructMember("sublanguageid", new XmlRpcValueBasic(info.sublanguageid))); + memberStruct.Members.Add(new XmlRpcStructMember("moviereleasename", new XmlRpcValueBasic(info.moviereleasename))); + memberStruct.Members.Add(new XmlRpcStructMember("movieaka", new XmlRpcValueBasic(info.movieaka))); + memberStruct.Members.Add(new XmlRpcStructMember("subauthorcomment", new XmlRpcValueBasic(info.subauthorcomment))); + // memberStruct.Members.Add(new XmlRpcStructMember("hearingimpaired", new XmlRpcValueBasic(info.hearingimpaired))); + // memberStruct.Members.Add(new XmlRpcStructMember("highdefinition", new XmlRpcValueBasic(info.highdefinition))); + // memberStruct.Members.Add(new XmlRpcStructMember("automatictranslation", new XmlRpcValueBasic(info.automatictranslation))); + member.Data = memberStruct; + s.Members.Add(member); + + // CDS members + int i = 1; + foreach (UploadSubtitleParameters cd in info.CDS) + { + XmlRpcStructMember member2 = new XmlRpcStructMember("cd" + i, null); + XmlRpcValueStruct memberStruct2 = new XmlRpcValueStruct(new List()); + memberStruct2.Members.Add(new XmlRpcStructMember("subhash", new XmlRpcValueBasic(cd.subhash))); + memberStruct2.Members.Add(new XmlRpcStructMember("subfilename", new XmlRpcValueBasic(cd.subfilename))); + memberStruct2.Members.Add(new XmlRpcStructMember("moviehash", new XmlRpcValueBasic(cd.moviehash))); + memberStruct2.Members.Add(new XmlRpcStructMember("moviebytesize", new XmlRpcValueBasic(cd.moviebytesize))); + memberStruct2.Members.Add(new XmlRpcStructMember("moviefps", new XmlRpcValueBasic(cd.moviefps))); + memberStruct2.Members.Add(new XmlRpcStructMember("movietimems", new XmlRpcValueBasic(cd.movietimems))); + memberStruct2.Members.Add(new XmlRpcStructMember("movieframes", new XmlRpcValueBasic(cd.movieframes))); + memberStruct2.Members.Add(new XmlRpcStructMember("moviefilename", new XmlRpcValueBasic(cd.moviefilename))); + memberStruct2.Members.Add(new XmlRpcStructMember("subcontent", new XmlRpcValueBasic(cd.subcontent))); + member2.Data = memberStruct2; + s.Members.Add(member2); + i++; + } + + // add main struct to parameters + parms.Add(s); + // add user agent + //parms.Add(new XmlRpcValueBasic(XML_PRC_USERAGENT)); + XmlRpcMethodCall call = new XmlRpcMethodCall("UploadSubtitles", parms); + OSHConsole.WriteLine("Sending UploadSubtitles request to the server ...", DebugCode.Good); + // Send the request to the server + string response = Utilities.GetStreamString(Utilities.SendRequest(XmlRpcGenerator.Generate(call), XML_PRC_USERAGENT)); + if (!response.Contains("ERROR:")) + { + // No error occur, get and decode the response. + XmlRpcMethodCall[] calls = XmlRpcGenerator.DecodeMethodResponse(response); + if (calls.Length > 0) + { + if (calls[0].Parameters.Count > 0) + { + XmlRpcValueStruct mainStruct = (XmlRpcValueStruct)calls[0].Parameters[0]; + // Create the response, we'll need it later + MethodResponseUploadSubtitles R = new MethodResponseUploadSubtitles(); + + // To make sure response is not currepted by server, do it in loop + foreach (XmlRpcStructMember MEMBER in mainStruct.Members) + { + switch (MEMBER.Name) + { + case "status": R.Status = (string)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "seconds": R.Seconds = (double)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "data": R.Data = (string)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "subtitles": R.SubTitles = (bool)MEMBER.Data.Data; OSHConsole.WriteLine(">" + MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + } + } + // Return the response to user !! + return R; + } + } + } + else + { + OSHConsole.WriteLine(response, DebugCode.Error); + return new MethodResponseError("Fail", response); + } + return new MethodResponseError("Fail", "UploadSubtitles call failed !"); + } + } +} diff --git a/OpenSubtitlesHandler/OpenSubtitlesHandler.csproj b/OpenSubtitlesHandler/OpenSubtitlesHandler.csproj new file mode 100644 index 0000000000..ba27c71408 --- /dev/null +++ b/OpenSubtitlesHandler/OpenSubtitlesHandler.csproj @@ -0,0 +1,123 @@ + + + + + Debug + AnyCPU + {4A4402D4-E910-443B-B8FC-2C18286A2CA0} + Library + Properties + OpenSubtitlesHandler + OpenSubtitlesHandler + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {9142eefa-7570-41e1-bfcc-468bb571af2f} + MediaBrowser.Common + + + {7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b} + MediaBrowser.Model + + + + + + + + \ No newline at end of file diff --git a/OpenSubtitlesHandler/OtherTypes/GetAvailableTranslationsResult.cs b/OpenSubtitlesHandler/OtherTypes/GetAvailableTranslationsResult.cs new file mode 100644 index 0000000000..ae6317b4d5 --- /dev/null +++ b/OpenSubtitlesHandler/OtherTypes/GetAvailableTranslationsResult.cs @@ -0,0 +1,39 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +namespace OpenSubtitlesHandler +{ + public struct GetAvailableTranslationsResult + { + private string _language; + private string _LastCreated; + private string _StringsNo; + + public string LanguageID { get { return _language; } set { _language = value; } } + public string LastCreated { get { return _LastCreated; } set { _LastCreated = value; } } + public string StringsNo { get { return _StringsNo; } set { _StringsNo = value; } } + /// + /// LanguageID (LastCreated) + /// + /// + public override string ToString() + { + return _language + " (" + _LastCreated + ")"; + } + } +} diff --git a/OpenSubtitlesHandler/OtherTypes/GetCommentsResult.cs b/OpenSubtitlesHandler/OtherTypes/GetCommentsResult.cs new file mode 100644 index 0000000000..2eedd25380 --- /dev/null +++ b/OpenSubtitlesHandler/OtherTypes/GetCommentsResult.cs @@ -0,0 +1,37 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; + +namespace OpenSubtitlesHandler +{ + public struct GetCommentsResult + { + private string _IDSubtitle; + private string _UserID; + private string _UserNickName; + private string _Comment; + private string _Created; + + public string IDSubtitle { get { return _IDSubtitle; } set { _IDSubtitle = value; } } + public string UserID { get { return _UserID; } set { _UserID = value; } } + public string UserNickName { get { return _UserNickName; } set { _UserNickName = value; } } + public string Comment { get { return _Comment; } set { _Comment = value; } } + public string Created { get { return _Created; } set { _Created = value; } } + } +} diff --git a/OpenSubtitlesHandler/Properties/AssemblyInfo.cs b/OpenSubtitlesHandler/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..9fad43d749 --- /dev/null +++ b/OpenSubtitlesHandler/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("OpenSubtitlesHandler")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("OpenSubtitlesHandler")] +[assembly: AssemblyCopyright("Copyright © 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("7c43bda2-2037-449d-8aac-9c0ccee8191f")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/OpenSubtitlesHandler/Readme.txt b/OpenSubtitlesHandler/Readme.txt new file mode 100644 index 0000000000..42b4fdab3c --- /dev/null +++ b/OpenSubtitlesHandler/Readme.txt @@ -0,0 +1,20 @@ +OpenSubtitlesHandler +==================== +This project is for OpenSubtitles.org integration‏. The point is to allow user to access OpenSubtitles.org database directly +within ASM without the need to open internet browser. +The plan: Implement the "OSDb protocol" http://trac.opensubtitles.org/projects/opensubtitles/wiki/OSDb + +Copyright: +========= +This library ann all its content are written by Ala Ibrahim Hadid. +Copyright © Ala Ibrahim Hadid 2013 +mailto:ahdsoftwares@hotmail.com + +Resources: +========== +* GetHash.dll: this dll is used to compute hash for movie. + For more information please visit http://trac.opensubtitles.org/projects/opensubtitles/wiki/HashSourceCodes#C2 + +XML_RPC: +======== +This class is created to generate XML-RPC requests as XML String. All you need is to call XML_RPC.Generate() method. \ No newline at end of file diff --git a/OpenSubtitlesHandler/SubtitleTypes/CheckSubHashResult.cs b/OpenSubtitlesHandler/SubtitleTypes/CheckSubHashResult.cs new file mode 100644 index 0000000000..0e77601ba8 --- /dev/null +++ b/OpenSubtitlesHandler/SubtitleTypes/CheckSubHashResult.cs @@ -0,0 +1,31 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; + +namespace OpenSubtitlesHandler +{ + public struct CheckSubHashResult + { + private string _hash; + private string _id; + + public string Hash { get { return _hash; } set { _hash = value; } } + public string SubID { get { return _id; } set { _id = value; } } + } +} diff --git a/OpenSubtitlesHandler/SubtitleTypes/SubtitleDownloadResult.cs b/OpenSubtitlesHandler/SubtitleTypes/SubtitleDownloadResult.cs new file mode 100644 index 0000000000..35bf796e6a --- /dev/null +++ b/OpenSubtitlesHandler/SubtitleTypes/SubtitleDownloadResult.cs @@ -0,0 +1,44 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; + +namespace OpenSubtitlesHandler +{ + public struct SubtitleDownloadResult + { + private string idsubtitlefile; + private string data; + + public string IdSubtitleFile + { get { return idsubtitlefile; } set { idsubtitlefile = value; } } + /// + /// Get or set the data of subtitle file. To decode, decode the string to base64 and then decompress with GZIP. + /// + public string Data + { get { return data; } set { data = value; } } + /// + /// IdSubtitleFile + /// + /// + public override string ToString() + { + return idsubtitlefile.ToString(); + } + } +} diff --git a/OpenSubtitlesHandler/SubtitleTypes/SubtitleLanguage.cs b/OpenSubtitlesHandler/SubtitleTypes/SubtitleLanguage.cs new file mode 100644 index 0000000000..511c986704 --- /dev/null +++ b/OpenSubtitlesHandler/SubtitleTypes/SubtitleLanguage.cs @@ -0,0 +1,39 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +namespace OpenSubtitlesHandler +{ + public struct SubtitleLanguage + { + private string _SubLanguageID; + private string _LanguageName; + private string _ISO639; + + public string SubLanguageID { get { return _SubLanguageID; } set { _SubLanguageID = value; } } + public string LanguageName { get { return _LanguageName; } set { _LanguageName = value; } } + public string ISO639 { get { return _ISO639; } set { _ISO639 = value; } } + /// + /// LanguageName [SubLanguageID] + /// + /// LanguageName [SubLanguageID] + public override string ToString() + { + return _LanguageName + " [" + _SubLanguageID + "]"; + } + } +} diff --git a/OpenSubtitlesHandler/SubtitleTypes/SubtitleSearchParameters.cs b/OpenSubtitlesHandler/SubtitleTypes/SubtitleSearchParameters.cs new file mode 100644 index 0000000000..e11ff1b9a9 --- /dev/null +++ b/OpenSubtitlesHandler/SubtitleTypes/SubtitleSearchParameters.cs @@ -0,0 +1,115 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; + +namespace OpenSubtitlesHandler +{ + /// + /// Paramaters for subtitle search call + /// + public struct SubtitleSearchParameters + { + /// + /// Paramaters for subtitle search call + /// + /// List of language ISO639-3 language codes to search for, divided by ',' (e.g. 'cze,eng,slo') + /// Video file hash as calculated by one of the implementation functions as seen on http://trac.opensubtitles.org/projects/opensubtitles/wiki/HashSourceCodes + /// Size of video file in bytes + public SubtitleSearchParameters(string subLanguageId, string movieHash, long movieByteSize) + { + this.subLanguageId = subLanguageId; + this.movieHash = movieHash; + this.movieByteSize = movieByteSize; + this.imdbid = ""; + this._episode = ""; + this._season = ""; + this._query = ""; + } + + public SubtitleSearchParameters(string subLanguageId, string query, string season, string episode) + { + this.subLanguageId = subLanguageId; + this.movieHash = ""; + this.movieByteSize = 0; + this.imdbid = ""; + this._episode = episode; + this._season = season; + this._query = query; + } + + /// + /// Paramaters for subtitle search call + /// + /// List of language ISO639-3 language codes to search for, divided by ',' (e.g. 'cze,eng,slo') + /// Video file hash as calculated by one of the implementation functions as seen on http://trac.opensubtitles.org/projects/opensubtitles/wiki/HashSourceCodes + /// Size of video file in bytes + /// IMDb ID of movie this video is part of, belongs to. + public SubtitleSearchParameters(string subLanguageId, string movieHash, long movieByteSize, string imdbid) + { + this.subLanguageId = subLanguageId; + this.movieHash = movieHash; + this.movieByteSize = movieByteSize; + this.imdbid = imdbid; + this._episode = ""; + this._season = ""; + this._query = ""; + } + + private string subLanguageId; + private string movieHash; + private long movieByteSize; + private string imdbid; + private string _query; + private string _episode; + + public string Episode { + get { return _episode; } + set { _episode = value; } + } + + public string Season { + get { return _season; } + set { _season = value; } + } + + private string _season; + + public string Query { + get { return _query; } + set { _query = value; } + } + + /// + /// List of language ISO639-3 language codes to search for, divided by ',' (e.g. 'cze,eng,slo') + /// + public string SubLangaugeID { get { return subLanguageId; } set { subLanguageId = value; } } + /// + /// Video file hash as calculated by one of the implementation functions as seen on http://trac.opensubtitles.org/projects/opensubtitles/wiki/HashSourceCodes + /// + public string MovieHash { get { return movieHash; } set { movieHash = value; } } + /// + /// Size of video file in bytes + /// + public long MovieByteSize { get { return movieByteSize; } set { movieByteSize = value; } } + /// + /// ​IMDb ID of movie this video is part of, belongs to. + /// + public string IMDbID { get { return imdbid; } set { imdbid = value; } } + } +} diff --git a/OpenSubtitlesHandler/SubtitleTypes/SubtitleSearchResult.cs b/OpenSubtitlesHandler/SubtitleTypes/SubtitleSearchResult.cs new file mode 100644 index 0000000000..a56a6edaba --- /dev/null +++ b/OpenSubtitlesHandler/SubtitleTypes/SubtitleSearchResult.cs @@ -0,0 +1,137 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; + +namespace OpenSubtitlesHandler +{ + /// + /// The subtitle search result that comes with server response on SearchSubtitles successed call + /// + public struct SubtitleSearchResult + { + private string _IDSubMovieFile; + private string _MovieHash; + private string _MovieByteSize; + private string _MovieTimeMS; + private string _IDSubtitleFile; + private string _SubFileName; + private string _SubActualCD; + private string _SubSize; + private string _SubHash; + private string _IDSubtitle; + private string _UserID; + private string _SubLanguageID; + private string _SubFormat; + private string _SeriesSeason; + private string _SeriesEpisode; + private string _SubSumCD; + private string _SubAuthorComment; + private string _SubAddDate; + private string _SubBad; + private string _SubRating; + private string _SubDownloadsCnt; + private string _MovieReleaseName; + private string _IDMovie; + private string _IDMovieImdb; + private string _MovieName; + private string _MovieNameEng; + private string _MovieYear; + private string _MovieImdbRating; + private string _UserNickName; + private string _ISO639; + private string _LanguageName; + private string _SubDownloadLink; + private string _ZipDownloadLink; + + public string IDSubMovieFile + { get { return _IDSubMovieFile; } set { _IDSubMovieFile = value; } } + public string MovieHash + { get { return _MovieHash; } set { _MovieHash = value; } } + public string MovieByteSize + { get { return _MovieByteSize; } set { _MovieByteSize = value; } } + public string MovieTimeMS + { get { return _MovieTimeMS; } set { _MovieTimeMS = value; } } + public string IDSubtitleFile + { get { return _IDSubtitleFile; } set { _IDSubtitleFile = value; } } + public string SubFileName + { get { return _SubFileName; } set { _SubFileName = value; } } + public string SubActualCD + { get { return _SubActualCD; } set { _SubActualCD = value; } } + public string SubSize + { get { return _SubSize; } set { _SubSize = value; } } + public string SubHash + { get { return _SubHash; } set { _SubHash = value; } } + public string IDSubtitle + { get { return _IDSubtitle; } set { _IDSubtitle = value; } } + public string UserID + { get { return _UserID; } set { _UserID = value; } } + public string SubLanguageID + { get { return _SubLanguageID; } set { _SubLanguageID = value; } } + public string SubFormat + { get { return _SubFormat; } set { _SubFormat = value; } } + public string SubSumCD + { get { return _SubSumCD; } set { _SubSumCD = value; } } + public string SubAuthorComment + { get { return _SubAuthorComment; } set { _SubAuthorComment = value; } } + public string SubAddDate + { get { return _SubAddDate; } set { _SubAddDate = value; } } + public string SubBad + { get { return _SubBad; } set { _SubBad = value; } } + public string SubRating + { get { return _SubRating; } set { _SubRating = value; } } + public string SubDownloadsCnt + { get { return _SubDownloadsCnt; } set { _SubDownloadsCnt = value; } } + public string MovieReleaseName + { get { return _MovieReleaseName; } set { _MovieReleaseName = value; } } + public string IDMovie + { get { return _IDMovie; } set { _IDMovie = value; } } + public string IDMovieImdb + { get { return _IDMovieImdb; } set { _IDMovieImdb = value; } } + public string MovieName + { get { return _MovieName; } set { _MovieName = value; } } + public string MovieNameEng + { get { return _MovieNameEng; } set { _MovieNameEng = value; } } + public string MovieYear + { get { return _MovieYear; } set { _MovieYear = value; } } + public string MovieImdbRating + { get { return _MovieImdbRating; } set { _MovieImdbRating = value; } } + public string UserNickName + { get { return _UserNickName; } set { _UserNickName = value; } } + public string ISO639 + { get { return _ISO639; } set { _ISO639 = value; } } + public string LanguageName + { get { return _LanguageName; } set { _LanguageName = value; } } + public string SubDownloadLink + { get { return _SubDownloadLink; } set { _SubDownloadLink = value; } } + public string ZipDownloadLink + { get { return _ZipDownloadLink; } set { _ZipDownloadLink = value; } } + public string SeriesSeason + { get { return _SeriesSeason; } set { _SeriesSeason = value; } } + public string SeriesEpisode + { get { return _SeriesEpisode; } set { _SeriesEpisode = value; } } + /// + /// SubFileName + " (" + SubFormat + ")" + /// + /// + public override string ToString() + { + return _SubFileName + " (" + _SubFormat + ")"; + } + } +} diff --git a/OpenSubtitlesHandler/SubtitleTypes/TryUploadSubtitlesParameters.cs b/OpenSubtitlesHandler/SubtitleTypes/TryUploadSubtitlesParameters.cs new file mode 100644 index 0000000000..a95d151eb2 --- /dev/null +++ b/OpenSubtitlesHandler/SubtitleTypes/TryUploadSubtitlesParameters.cs @@ -0,0 +1,48 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; + +namespace OpenSubtitlesHandler +{ + public class TryUploadSubtitlesParameters + { + private string _subhash = ""; + private string _subfilename = ""; + private string _moviehash = ""; + private string _moviebytesize = ""; + private int _movietimems = 0; + private int _movieframes = 0; + private double _moviefps = 0; + private string _moviefilename = ""; + + public string subhash { get { return _subhash; } set { _subhash = value; } } + public string subfilename { get { return _subfilename; } set { _subfilename = value; } } + public string moviehash { get { return _moviehash; } set { _moviehash = value; } } + public string moviebytesize { get { return _moviebytesize; } set { _moviebytesize = value; } } + public int movietimems { get { return _movietimems; } set { _movietimems = value; } } + public int movieframes { get { return _movieframes; } set { _movieframes = value; } } + public double moviefps { get { return _moviefps; } set { _moviefps = value; } } + public string moviefilename { get { return _moviefilename; } set { _moviefilename = value; } } + + public override string ToString() + { + return _subfilename + " (" + _subhash + ")"; + } + } +} diff --git a/OpenSubtitlesHandler/SubtitleTypes/UploadSubtitleInfoParameter.cs b/OpenSubtitlesHandler/SubtitleTypes/UploadSubtitleInfoParameter.cs new file mode 100644 index 0000000000..8e147878ba --- /dev/null +++ b/OpenSubtitlesHandler/SubtitleTypes/UploadSubtitleInfoParameter.cs @@ -0,0 +1,46 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; +using System.Collections.Generic; + +namespace OpenSubtitlesHandler +{ + public class UploadSubtitleInfoParameters + { + private string _idmovieimdb; + private string _moviereleasename; + private string _movieaka; + private string _sublanguageid; + private string _subauthorcomment; + private bool _hearingimpaired; + private bool _highdefinition; + private bool _automatictranslation; + private List cds; + + public string idmovieimdb { get { return _idmovieimdb; } set { _idmovieimdb = value; } } + public string moviereleasename { get { return _moviereleasename; } set { _moviereleasename = value; } } + public string movieaka { get { return _movieaka; } set { _movieaka = value; } } + public string sublanguageid { get { return _sublanguageid; } set { _sublanguageid = value; } } + public string subauthorcomment { get { return _subauthorcomment; } set { _subauthorcomment = value; } } + public bool hearingimpaired { get { return _hearingimpaired; } set { _hearingimpaired = value; } } + public bool highdefinition { get { return _highdefinition; } set { _highdefinition = value; } } + public bool automatictranslation { get { return _automatictranslation; } set { _automatictranslation = value; } } + public List CDS { get { return cds; } set { cds = value; } } + } +} diff --git a/OpenSubtitlesHandler/SubtitleTypes/UploadSubtitleParameters.cs b/OpenSubtitlesHandler/SubtitleTypes/UploadSubtitleParameters.cs new file mode 100644 index 0000000000..90a2739599 --- /dev/null +++ b/OpenSubtitlesHandler/SubtitleTypes/UploadSubtitleParameters.cs @@ -0,0 +1,53 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; + +namespace OpenSubtitlesHandler +{ + public struct UploadSubtitleParameters + { + private string _subhash; + private string _subfilename; + private string _moviehash; + private double _moviebytesize; + private int _movietimems; + private int _movieframes; + private double _moviefps; + private string _moviefilename; + private string _subcontent; + + public string subhash { get { return _subhash; } set { _subhash = value; } } + public string subfilename { get { return _subfilename; } set { _subfilename = value; } } + public string moviehash { get { return _moviehash; } set { _moviehash = value; } } + public double moviebytesize { get { return _moviebytesize; } set { _moviebytesize = value; } } + public int movietimems { get { return _movietimems; } set { _movietimems = value; } } + public int movieframes { get { return _movieframes; } set { _movieframes = value; } } + public double moviefps { get { return _moviefps; } set { _moviefps = value; } } + public string moviefilename { get { return _moviefilename; } set { _moviefilename = value; } } + /// + /// Sub content. Note: this value must be the subtitle file gziped and then base64 decoded. + /// + public string subcontent { get { return _subcontent; } set { _subcontent = value; } } + + public override string ToString() + { + return _subfilename + " (" + _subhash + ")"; + } + } +} diff --git a/OpenSubtitlesHandler/Utilities.cs b/OpenSubtitlesHandler/Utilities.cs new file mode 100644 index 0000000000..5c72f4fde7 --- /dev/null +++ b/OpenSubtitlesHandler/Utilities.cs @@ -0,0 +1,235 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using System.IO.Compression; +using System.Net; +using System.Security.Cryptography; +using System.Threading.Tasks; +using MediaBrowser.Common.Net; + +namespace OpenSubtitlesHandler +{ + /// + /// Include helper methods. All member are statics. + /// + public sealed class Utilities + { + private const string XML_RPC_SERVER = "http://api.opensubtitles.org/xml-rpc"; + + /// + /// Compute movie hash + /// + /// The complete media file path + /// The hash as Hexadecimal string + public static string ComputeHash(string fileName) + { + byte[] hash = MovieHasher.ComputeMovieHash(fileName); + return MovieHasher.ToHexadecimal(hash); + } + /// + /// Compute md5 for a file + /// + /// The complete file path + /// MD5 of the file + public static string ComputeMd5(string filename) + { + var md5 = MD5.Create(); + var sb = new StringBuilder(); + Stream str = new FileStream(filename, FileMode.Open, FileAccess.Read); + + foreach (var b in md5.ComputeHash(str)) + sb.Append(b.ToString("x2").ToLower()); + str.Close(); + return sb.ToString(); + } + /// + /// Decompress data using GZip + /// + /// The stream that hold the data + /// Bytes array of decompressed data + public static byte[] Decompress(Stream dataToDecompress) + { + MemoryStream target = new MemoryStream(); + + using (System.IO.Compression.GZipStream decompressionStream = new System.IO.Compression.GZipStream(dataToDecompress, + System.IO.Compression.CompressionMode.Decompress)) + { + decompressionStream.CopyTo(target); + } + return target.GetBuffer(); + + } + + /// + /// Compress data using GZip (the retunred buffer will be WITHOUT HEADER) + /// + /// The stream that hold the data + /// Bytes array of compressed data WITHOUT HEADER bytes + public static byte[] Compress(Stream dataToCompress) + { + /*using (var compressed = new MemoryStream()) + { + using (var compressor = new System.IO.Compression.GZipStream(compressed, + System.IO.Compression.CompressionMode.Compress)) + { + dataToCompress.CopyTo(compressor); + } + // Get the compressed bytes only after closing the GZipStream + return compressed.ToArray(); + }*/ + //using (var compressedOutput = new MemoryStream()) + //{ + // using (var compressedStream = new ZlibStream(compressedOutput, + // Ionic.Zlib.CompressionMode.Compress, + // CompressionLevel.Default, false)) + // { + // var buffer = new byte[4096]; + // int byteCount; + // do + // { + // byteCount = dataToCompress.Read(buffer, 0, buffer.Length); + + // if (byteCount > 0) + // { + // compressedStream.Write(buffer, 0, byteCount); + // } + // } while (byteCount > 0); + // } + // return compressedOutput.ToArray(); + //} + + throw new NotImplementedException(); + } + + /// + /// Handle server response stream and decode it as given encoding string. + /// + /// The response stream. Expects a stream that doesn't support seek. + /// The encoding that should be used to decode buffer + /// The string of the stream after decode using given encoding + public static string GetStreamString(Stream responseStream, Encoding encoding) + { + // Handle response, should be XML text. + List data = new List(); + while (true) + { + int r = responseStream.ReadByte(); + if (r < 0) + break; + data.Add((byte)r); + } + responseStream.Close(); + return encoding.GetString(data.ToArray()); + } + /// + /// Handle server response stream and decode it as ASCII encoding string. + /// + /// The response stream. Expects a stream that doesn't support seek. + /// The string of the stream after decode using ASCII encoding + public static string GetStreamString(Stream responseStream) + { + return GetStreamString(responseStream, Encoding.ASCII); + } + + public static IHttpClient HttpClient { get; set; } + + /// + /// Send a request to the server + /// + /// The request buffer to send as bytes array. + /// The user agent value. + /// Response of the server or stream of error message as string started with 'ERROR:' keyword. + public static Stream SendRequest(byte[] request, string userAgent) + { + return SendRequestAsync(request, userAgent).Result; + + //HttpWebRequest req = (HttpWebRequest)WebRequest.Create(XML_RPC_SERVER); + //req.ContentType = "text/xml"; + //req.Host = "api.opensubtitles.org:80"; + //req.Method = "POST"; + //req.UserAgent = "xmlrpc-epi-php/0.2 (PHP)"; + //req.ContentLength = request.Length; + //ServicePointManager.Expect100Continue = false; + //try + //{ + // using (Stream stm = req.GetRequestStream()) + // { + // stm.Write(request, 0, request.Length); + // } + + // WebResponse response = req.GetResponse(); + // return response.GetResponseStream(); + //} + //catch (Exception ex) + //{ + // Stream errorStream = new MemoryStream(); + // byte[] dd = Encoding.ASCII.GetBytes("ERROR: " + ex.Message); + // errorStream.Write(dd, 0, dd.Length); + // errorStream.Position = 0; + // return errorStream; + //} + } + + public static async Task SendRequestAsync(byte[] request, string userAgent) + { + var options = new HttpRequestOptions + { + RequestContentBytes = request, + RequestContentType = "text/xml", + UserAgent = "xmlrpc-epi-php/0.2 (PHP)", + Url = XML_RPC_SERVER + }; + + var result = await HttpClient.Post(options).ConfigureAwait(false); + + return result.Content; + + //HttpWebRequest req = (HttpWebRequest)WebRequest.Create(XML_RPC_SERVER); + //req.ContentType = "text/xml"; + //req.Host = "api.opensubtitles.org:80"; + //req.Method = "POST"; + //req.UserAgent = "xmlrpc-epi-php/0.2 (PHP)"; + //req.ContentLength = request.Length; + //ServicePointManager.Expect100Continue = false; + //try + //{ + // using (Stream stm = req.GetRequestStream()) + // { + // stm.Write(request, 0, request.Length); + // } + + // WebResponse response = req.GetResponse(); + // return response.GetResponseStream(); + //} + //catch (Exception ex) + //{ + // Stream errorStream = new MemoryStream(); + // byte[] dd = Encoding.ASCII.GetBytes("ERROR: " + ex.Message); + // errorStream.Write(dd, 0, dd.Length); + // errorStream.Position = 0; + // return errorStream; + //} + } + + } +} diff --git a/OpenSubtitlesHandler/XML-RPC/Docs/XML-RPC.txt b/OpenSubtitlesHandler/XML-RPC/Docs/XML-RPC.txt new file mode 100644 index 0000000000..8c84444a99 --- /dev/null +++ b/OpenSubtitlesHandler/XML-RPC/Docs/XML-RPC.txt @@ -0,0 +1,225 @@ +XML-RPC Specification + +Tue, Jun 15, 1999; by Dave Winer. + +Updated 6/30/03 DW + +Updated 10/16/99 DW + +Updated 1/21/99 DW + +This specification documents the XML-RPC protocol implemented in UserLand Frontier 5.1. + +For a non-technical explanation, see XML-RPC for Newbies. + +This page provides all the information that an implementor needs. + +Overview + +XML-RPC is a Remote Procedure Calling protocol that works over the Internet. + +An XML-RPC message is an HTTP-POST request. The body of the request is in XML. A procedure executes on the server and the value it returns is also formatted in XML. + +Procedure parameters can be scalars, numbers, strings, dates, etc.; and can also be complex record and list structures. + +Request example + +Here's an example of an XML-RPC request: + +POST /RPC2 HTTP/1.0 +User-Agent: Frontier/5.1.2 (WinNT) +Host: betty.userland.com +Content-Type: text/xml +Content-length: 181 + + + + examples.getStateName + + + + 41 + + + + + +Header requirements + +The format of the URI in the first line of the header is not specified. For example, it could be empty, a single slash, if the server is only handling XML-RPC calls. However, if the server is handling a mix of incoming HTTP requests, we allow the URI to help route the request to the code that handles XML-RPC requests. (In the example, the URI is /RPC2, telling the server to route the request to the "RPC2" responder.) + +A User-Agent and Host must be specified. + +The Content-Type is text/xml. + +The Content-Length must be specified and must be correct. + +Payload format + +The payload is in XML, a single structure. + +The must contain a sub-item, a string, containing the name of the method to be called. The string may only contain identifier characters, upper and lower-case A-Z, the numeric characters, 0-9, underscore, dot, colon and slash. It's entirely up to the server to decide how to interpret the characters in a methodName. + +For example, the methodName could be the name of a file containing a script that executes on an incoming request. It could be the name of a cell in a database table. Or it could be a path to a file contained within a hierarchy of folders and files. + +If the procedure call has parameters, the must contain a sub-item. The sub-item can contain any number of s, each of which has a . + +Scalar s + +s can be scalars, type is indicated by nesting the value inside one of the tags listed in this table: + +Tag Type Example + or four-byte signed integer -12 + 0 (false) or 1 (true) 1 + string hello world + double-precision signed floating point number -12.214 + date/time 19980717T14:08:55 + base64-encoded binary eW91IGNhbid0IHJlYWQgdGhpcyE= + +If no type is indicated, the type is string. + +s + +A value can also be of type . + +A contains s and each contains a and a . + +Here's an example of a two-element : + + + + lowerBound + 18 + + + upperBound + 139 + + + +s can be recursive, any may contain a or any other type, including an , described below. + +s + +A value can also be of type . + +An contains a single element, which can contain any number of s. + +Here's an example of a four-element array: + + + + 12 + Egypt + 0 + -31 + + + + elements do not have names. + +You can mix types as the example above illustrates. + +s can be recursive, any value may contain an or any other type, including a , described above. + +Response example + +Here's an example of a response to an XML-RPC request: + +HTTP/1.1 200 OK +Connection: close +Content-Length: 158 +Content-Type: text/xml +Date: Fri, 17 Jul 1998 19:55:08 GMT +Server: UserLand Frontier/5.1.2-WinNT + + South Dakota + +Response format + +Unless there's a lower-level error, always return 200 OK. + +The Content-Type is text/xml. Content-Length must be present and correct. + +The body of the response is a single XML structure, a , which can contain a single which contains a single which contains a single . + +The could also contain a which contains a which is a containing two elements, one named , an and one named , a . + +A can not contain both a and a . + +Fault example + +HTTP/1.1 200 OK +Connection: close +Content-Length: 426 +Content-Type: text/xml +Date: Fri, 17 Jul 1998 19:55:02 GMT +Server: UserLand Frontier/5.1.2-WinNT + + faultCode 4 faultString Too many parameters. + +Strategies/Goals + +Firewalls. The goal of this protocol is to lay a compatible foundation across different environments, no new power is provided beyond the capabilities of the CGI interface. Firewall software can watch for POSTs whose Content-Type is text/xml. + +Discoverability. We wanted a clean, extensible format that's very simple. It should be possible for an HTML coder to be able to look at a file containing an XML-RPC procedure call, understand what it's doing, and be able to modify it and have it work on the first or second try. + +Easy to implement. We also wanted it to be an easy to implement protocol that could quickly be adapted to run in other environments or on other operating systems. + +Updated 1/21/99 DW + +The following questions came up on the UserLand discussion group as XML-RPC was being implemented in Python. + + The Response Format section says "The body of the response is a single XML structure, a , which can contain a single ..." This is confusing. Can we leave out the ? + + No you cannot leave it out if the procedure executed successfully. There are only two options, either a response contains a structure or it contains a structure. That's why we used the word "can" in that sentence. + + Is "boolean" a distinct data type, or can boolean values be interchanged with integers (e.g. zero=false, non-zero=true)? + + Yes, boolean is a distinct data type. Some languages/environments allow for an easy coercion from zero to false and one to true, but if you mean true, send a boolean type with the value true, so your intent can't possibly be misunderstood. + + What is the legal syntax (and range) for integers? How to deal with leading zeros? Is a leading plus sign allowed? How to deal with whitespace? + + An integer is a 32-bit signed number. You can include a plus or minus at the beginning of a string of numeric characters. Leading zeros are collapsed. Whitespace is not permitted. Just numeric characters preceeded by a plus or minus. + + What is the legal syntax (and range) for floating point values (doubles)? How is the exponent represented? How to deal with whitespace? Can infinity and "not a number" be represented? + + There is no representation for infinity or negative infinity or "not a number". At this time, only decimal point notation is allowed, a plus or a minus, followed by any number of numeric characters, followed by a period and any number of numeric characters. Whitespace is not allowed. The range of allowable values is implementation-dependent, is not specified. + + What characters are allowed in strings? Non-printable characters? Null characters? Can a "string" be used to hold an arbitrary chunk of binary data? + + Any characters are allowed in a string except < and &, which are encoded as < and &. A string can be used to encode binary data. + + Does the "struct" element keep the order of keys. Or in other words, is the struct "foo=1, bar=2" equivalent to "bar=2, foo=1" or not? + + The struct element does not preserve the order of the keys. The two structs are equivalent. + + Can the struct contain other members than and ? Is there a global list of faultCodes? (so they can be mapped to distinct exceptions for languages like Python and Java)? + + A struct may not contain members other than those specified. This is true for all other structures. We believe the specification is flexible enough so that all reasonable data-transfer needs can be accomodated within the specified structures. If you believe strongly that this is not true, please post a message on the discussion group. + + There is no global list of fault codes. It is up to the server implementer, or higher-level standards to specify fault codes. + + What timezone should be assumed for the dateTime.iso8601 type? UTC? localtime? + + Don't assume a timezone. It should be specified by the server in its documentation what assumptions it makes about timezones. + +Additions + + type. 1/21/99 DW. + +Updated 6/30/03 DW + +Removed "ASCII" from definition of string. + +Changed copyright dates, below, to 1999-2003 from 1998-99. + +Copyright and disclaimer + +© Copyright 1998-2003 UserLand Software. All Rights Reserved. + +This document and translations of it may be copied and furnished to others, and derivative works that comment on or otherwise explain it or assist in its implementation may be prepared, copied, published and distributed, in whole or in part, without restriction of any kind, provided that the above copyright notice and these paragraphs are included on all such copies and derivative works. + +This document may not be modified in any way, such as by removing the copyright notice or references to UserLand or other organizations. Further, while these copyright restrictions apply to the written XML-RPC specification, no claim of ownership is made by UserLand to the protocol it describes. Any party may, for commercial or non-commercial purposes, implement this protocol without royalty or license fee to UserLand. The limited permissions granted herein are perpetual and will not be revoked by UserLand or its successors or assigns. + +This document and the information contained herein is provided on an "AS IS" basis and USERLAND DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. \ No newline at end of file diff --git a/OpenSubtitlesHandler/XML-RPC/Enums/XmlRpcValueType.cs b/OpenSubtitlesHandler/XML-RPC/Enums/XmlRpcValueType.cs new file mode 100644 index 0000000000..ef0c203ff1 --- /dev/null +++ b/OpenSubtitlesHandler/XML-RPC/Enums/XmlRpcValueType.cs @@ -0,0 +1,25 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +namespace XmlRpcHandler +{ + public enum XmlRpcBasicValueType + { + String, Int, Boolean, Double, dateTime_iso8601, base64 + } +} diff --git a/OpenSubtitlesHandler/XML-RPC/Types/XmlRpcMethodCall.cs b/OpenSubtitlesHandler/XML-RPC/Types/XmlRpcMethodCall.cs new file mode 100644 index 0000000000..1ec3622b23 --- /dev/null +++ b/OpenSubtitlesHandler/XML-RPC/Types/XmlRpcMethodCall.cs @@ -0,0 +1,63 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; +using System.Collections.Generic; + +namespace XmlRpcHandler +{ + /// + /// A method call + /// + public struct XmlRpcMethodCall + { + /// + /// A method call + /// + /// The name of this method + public XmlRpcMethodCall(string name) + { + this.name = name; + this.parameters = new List(); + } + /// + /// A method call + /// + /// The name of this method + /// A list of parameters + public XmlRpcMethodCall(string name, List parameters) + { + this.name = name; + this.parameters = parameters; + } + + private string name; + private List parameters; + + /// + /// Get or set the name of this method + /// + public string Name + { get { return name; } set { name = value; } } + /// + /// Get or set the parameters to be sent + /// + public List Parameters + { get { return parameters; } set { parameters = value; } } + } +} diff --git a/OpenSubtitlesHandler/XML-RPC/Values/IXmlRpcValue.cs b/OpenSubtitlesHandler/XML-RPC/Values/IXmlRpcValue.cs new file mode 100644 index 0000000000..19bc60347b --- /dev/null +++ b/OpenSubtitlesHandler/XML-RPC/Values/IXmlRpcValue.cs @@ -0,0 +1,37 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; + +namespace XmlRpcHandler +{ + public abstract class IXmlRpcValue + { + public IXmlRpcValue() + { } + public IXmlRpcValue(object data) + { + this.data = data; + } + private object data; + /// + /// Get or set the data of this value + /// + public virtual object Data { get { return data; } set { data = value; } } + } +} diff --git a/OpenSubtitlesHandler/XML-RPC/Values/XmlRpcStructMember.cs b/OpenSubtitlesHandler/XML-RPC/Values/XmlRpcStructMember.cs new file mode 100644 index 0000000000..12c86fdbfe --- /dev/null +++ b/OpenSubtitlesHandler/XML-RPC/Values/XmlRpcStructMember.cs @@ -0,0 +1,44 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; + +namespace XmlRpcHandler +{ + public class XmlRpcStructMember + { + public XmlRpcStructMember(string name, IXmlRpcValue data) + { + this.name = name; + this.data = data; + } + private string name; + private IXmlRpcValue data; + + /// + /// Get or set the name of this member + /// + public string Name + { get { return name; } set { name = value; } } + /// + /// Get or set the data of this member + /// + public IXmlRpcValue Data + { get { return data; } set { data = value; } } + } +} diff --git a/OpenSubtitlesHandler/XML-RPC/Values/XmlRpcValueArray.cs b/OpenSubtitlesHandler/XML-RPC/Values/XmlRpcValueArray.cs new file mode 100644 index 0000000000..8fd8b5ca03 --- /dev/null +++ b/OpenSubtitlesHandler/XML-RPC/Values/XmlRpcValueArray.cs @@ -0,0 +1,121 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; +using System.Collections.Generic; + +namespace XmlRpcHandler +{ + public class XmlRpcValueArray : IXmlRpcValue + { + public XmlRpcValueArray() : + base() + { + values = new List(); + } + public XmlRpcValueArray(object data) : + base(data) + { + values = new List(); + } + public XmlRpcValueArray(string[] texts) : + base() + { + values = new List(); + foreach (string val in texts) + { + values.Add(new XmlRpcValueBasic(val)); + } + } + public XmlRpcValueArray(int[] ints) : + base() + { + values = new List(); + foreach (int val in ints) + { + values.Add(new XmlRpcValueBasic(val)); + } + } + public XmlRpcValueArray(double[] doubles) : + base() + { + values = new List(); + foreach (double val in doubles) + { + values.Add(new XmlRpcValueBasic(val)); + } + } + public XmlRpcValueArray(bool[] bools) : + base() + { + values = new List(); + foreach (bool val in bools) + { + values.Add(new XmlRpcValueBasic(val)); + } + } + public XmlRpcValueArray(long[] base24s) : + base() + { + values = new List(); + foreach (long val in base24s) + { + values.Add(new XmlRpcValueBasic(val)); + } + } + public XmlRpcValueArray(DateTime[] dates) : + base() + { + values = new List(); + foreach (DateTime val in dates) + { + values.Add(new XmlRpcValueBasic(val)); + } + } + public XmlRpcValueArray(XmlRpcValueBasic[] basicValues) : + base() + { + values = new List(); + foreach (XmlRpcValueBasic val in basicValues) + { + values.Add(val); + } + } + public XmlRpcValueArray(XmlRpcValueStruct[] structs) : + base() + { + values = new List(); + foreach (XmlRpcValueStruct val in structs) + { + values.Add(val); + } + } + public XmlRpcValueArray(XmlRpcValueArray[] arrays) : + base() + { + values = new List(); + foreach (XmlRpcValueArray val in arrays) + { + values.Add(val); + } + } + private List values; + + public List Values { get { return values; } set { values = value; } } + } +} diff --git a/OpenSubtitlesHandler/XML-RPC/Values/XmlRpcValueBasic.cs b/OpenSubtitlesHandler/XML-RPC/Values/XmlRpcValueBasic.cs new file mode 100644 index 0000000000..2827283ff8 --- /dev/null +++ b/OpenSubtitlesHandler/XML-RPC/Values/XmlRpcValueBasic.cs @@ -0,0 +1,100 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; +using System.Collections.Generic; + +namespace XmlRpcHandler +{ + public class XmlRpcValueBasic : IXmlRpcValue + { + public XmlRpcValueBasic() + : base() + { } + public XmlRpcValueBasic(string data) + : base(data) + { this.type = XmlRpcBasicValueType.String; } + public XmlRpcValueBasic(int data) + : base(data) + { this.type = XmlRpcBasicValueType.Int; } + public XmlRpcValueBasic(double data) + : base(data) + { this.type = XmlRpcBasicValueType.Double; } + public XmlRpcValueBasic(DateTime data) + : base(data) + { this.type = XmlRpcBasicValueType.dateTime_iso8601; } + public XmlRpcValueBasic(bool data) + : base(data) + { this.type = XmlRpcBasicValueType.Boolean; } + public XmlRpcValueBasic(long data) + : base(data) + { this.type = XmlRpcBasicValueType.base64; } + public XmlRpcValueBasic(object data, XmlRpcBasicValueType type) + : base(data) + { this.type = type; } + + private XmlRpcBasicValueType type = XmlRpcBasicValueType.String; + /// + /// Get or set the type of this basic value + /// + public XmlRpcBasicValueType ValueType { get { return type; } set { type = value; } } + /*Oprators. help a lot.*/ + public static implicit operator string(XmlRpcValueBasic f) + { + if (f.type == XmlRpcBasicValueType.String) + return f.Data.ToString(); + else + throw new Exception("Unable to convert, this value is not string type."); + } + public static implicit operator int(XmlRpcValueBasic f) + { + if (f.type == XmlRpcBasicValueType.String) + return (int)f.Data; + else + throw new Exception("Unable to convert, this value is not int type."); + } + public static implicit operator double(XmlRpcValueBasic f) + { + if (f.type == XmlRpcBasicValueType.String) + return (double)f.Data; + else + throw new Exception("Unable to convert, this value is not double type."); + } + public static implicit operator bool(XmlRpcValueBasic f) + { + if (f.type == XmlRpcBasicValueType.String) + return (bool)f.Data; + else + throw new Exception("Unable to convert, this value is not bool type."); + } + public static implicit operator long(XmlRpcValueBasic f) + { + if (f.type == XmlRpcBasicValueType.String) + return (long)f.Data; + else + throw new Exception("Unable to convert, this value is not long type."); + } + public static implicit operator DateTime(XmlRpcValueBasic f) + { + if (f.type == XmlRpcBasicValueType.String) + return (DateTime)f.Data; + else + throw new Exception("Unable to convert, this value is not DateTime type."); + } + } +} diff --git a/OpenSubtitlesHandler/XML-RPC/Values/XmlRpcValueStruct.cs b/OpenSubtitlesHandler/XML-RPC/Values/XmlRpcValueStruct.cs new file mode 100644 index 0000000000..9e4cf6056e --- /dev/null +++ b/OpenSubtitlesHandler/XML-RPC/Values/XmlRpcValueStruct.cs @@ -0,0 +1,43 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; +using System.Collections.Generic; + +namespace XmlRpcHandler +{ + /// + /// Represents XML-RPC struct + /// + public class XmlRpcValueStruct : IXmlRpcValue + { + /// + /// Represents XML-RPC struct + /// + /// + public XmlRpcValueStruct(List members) + { this.members = members; } + + private List members; + /// + /// Get or set the members collection + /// + public List Members + { get { return members; } set { members = value; } } + } +} diff --git a/OpenSubtitlesHandler/XML-RPC/XmlRpcGenerator.cs b/OpenSubtitlesHandler/XML-RPC/XmlRpcGenerator.cs new file mode 100644 index 0000000000..028b734dc5 --- /dev/null +++ b/OpenSubtitlesHandler/XML-RPC/XmlRpcGenerator.cs @@ -0,0 +1,323 @@ +/* This file is part of OpenSubtitles Handler + A library that handle OpenSubtitles.org XML-RPC methods. + + Copyright © Ala Ibrahim Hadid 2013 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +using System; +using System.Text; +using System.Collections.Generic; +using System.IO; +using System.Xml; +namespace XmlRpcHandler +{ + /// + /// A class for making XML-RPC requests. The requests then can be sent directly as http request. + /// + public class XmlRpcGenerator + { + /// + /// Generate XML-RPC request using method call. + /// + /// The method call + /// The request in xml. + public static byte[] Generate(XmlRpcMethodCall method) + { return Generate(new XmlRpcMethodCall[] { method }); } + /// + /// Generate XML-RPC request using method calls array. + /// + /// The method calls array + /// The request in utf8 xml string as buffer + public static byte[] Generate(XmlRpcMethodCall[] methods) + { + if (methods == null) + throw new Exception("No method to write !"); + if (methods.Length == 0) + throw new Exception("No method to write !"); + // Create xml + XmlWriterSettings sett = new XmlWriterSettings(); + sett.Indent = true; + + sett.Encoding = Encoding.UTF8; + FileStream str = new FileStream(Path.GetTempPath() + "\\request.xml", FileMode.Create, FileAccess.Write); + + XmlWriter XMLwrt = XmlWriter.Create(str, sett); + // Let's write the methods + foreach (XmlRpcMethodCall method in methods) + { + XMLwrt.WriteStartElement("methodCall");//methodCall + XMLwrt.WriteStartElement("methodName");//methodName + XMLwrt.WriteString(method.Name); + XMLwrt.WriteEndElement();//methodName + XMLwrt.WriteStartElement("params");//params + // Write values + foreach (IXmlRpcValue p in method.Parameters) + { + XMLwrt.WriteStartElement("param");//param + if (p is XmlRpcValueBasic) + { + WriteBasicValue(XMLwrt, (XmlRpcValueBasic)p); + } + else if (p is XmlRpcValueStruct) + { + WriteStructValue(XMLwrt, (XmlRpcValueStruct)p); + } + else if (p is XmlRpcValueArray) + { + WriteArrayValue(XMLwrt, (XmlRpcValueArray)p); + } + XMLwrt.WriteEndElement();//param + } + + XMLwrt.WriteEndElement();//params + XMLwrt.WriteEndElement();//methodCall + } + XMLwrt.Flush(); + XMLwrt.Close(); + str.Close(); + string requestContent = File.ReadAllText(Path.GetTempPath() + "\\request.xml"); + return Encoding.UTF8.GetBytes(requestContent); + } + /// + /// Decode response then return the values + /// + /// The response xml string as provided by server as methodResponse + /// + public static XmlRpcMethodCall[] DecodeMethodResponse(string xmlResponse) + { + List methods = new List(); + XmlReaderSettings sett = new XmlReaderSettings(); + sett.DtdProcessing = DtdProcessing.Ignore; + sett.IgnoreWhitespace = true; + MemoryStream str = new MemoryStream(Encoding.ASCII.GetBytes(xmlResponse)); + if (xmlResponse.Contains(@"encoding=""utf-8""")) + { + str = new MemoryStream(Encoding.UTF8.GetBytes(xmlResponse)); + } + XmlReader XMLread = XmlReader.Create(str, sett); + + XmlRpcMethodCall call = new XmlRpcMethodCall("methodResponse"); + // Read parameters + while (XMLread.Read()) + { + if (XMLread.Name == "param" && XMLread.IsStartElement()) + { + IXmlRpcValue val = ReadValue(XMLread); + if (val != null) + call.Parameters.Add(val); + } + } + methods.Add(call); + XMLread.Close(); + return methods.ToArray(); + } + + private static void WriteBasicValue(XmlWriter XMLwrt, XmlRpcValueBasic val) + { + XMLwrt.WriteStartElement("value");//value + switch (val.ValueType) + { + case XmlRpcBasicValueType.String: + XMLwrt.WriteStartElement("string"); + if (val.Data != null) + XMLwrt.WriteString(val.Data.ToString()); + XMLwrt.WriteEndElement(); + break; + case XmlRpcBasicValueType.Int: + XMLwrt.WriteStartElement("int"); + if (val.Data != null) + XMLwrt.WriteString(val.Data.ToString()); + XMLwrt.WriteEndElement(); + break; + case XmlRpcBasicValueType.Boolean: + XMLwrt.WriteStartElement("boolean"); + if (val.Data != null) + XMLwrt.WriteString(((bool)val.Data) ? "1" : "0"); + XMLwrt.WriteEndElement(); + break; + case XmlRpcBasicValueType.Double: + XMLwrt.WriteStartElement("double"); + if (val.Data != null) + XMLwrt.WriteString(val.Data.ToString()); + XMLwrt.WriteEndElement(); + break; + case XmlRpcBasicValueType.dateTime_iso8601: + XMLwrt.WriteStartElement("dateTime.iso8601"); + // Get date time format + if (val.Data != null) + { + DateTime time = (DateTime)val.Data; + string dt = time.Year + time.Month.ToString("D2") + time.Day.ToString("D2") + + "T" + time.Hour.ToString("D2") + ":" + time.Minute.ToString("D2") + ":" + + time.Second.ToString("D2"); + XMLwrt.WriteString(dt); + } + XMLwrt.WriteEndElement(); + break; + case XmlRpcBasicValueType.base64: + XMLwrt.WriteStartElement("base64"); + if (val.Data != null) + XMLwrt.WriteString(Convert.ToBase64String(BitConverter.GetBytes((long)val.Data))); + XMLwrt.WriteEndElement(); + break; + } + XMLwrt.WriteEndElement();//value + } + private static void WriteStructValue(XmlWriter XMLwrt, XmlRpcValueStruct val) + { + XMLwrt.WriteStartElement("value");//value + XMLwrt.WriteStartElement("struct");//struct + foreach (XmlRpcStructMember member in val.Members) + { + XMLwrt.WriteStartElement("member");//member + + XMLwrt.WriteStartElement("name");//name + XMLwrt.WriteString(member.Name); + XMLwrt.WriteEndElement();//name + + if (member.Data is XmlRpcValueBasic) + { + WriteBasicValue(XMLwrt, (XmlRpcValueBasic)member.Data); + } + else if (member.Data is XmlRpcValueStruct) + { + WriteStructValue(XMLwrt, (XmlRpcValueStruct)member.Data); + } + else if (member.Data is XmlRpcValueArray) + { + WriteArrayValue(XMLwrt, (XmlRpcValueArray)member.Data); + } + + XMLwrt.WriteEndElement();//member + } + XMLwrt.WriteEndElement();//struct + XMLwrt.WriteEndElement();//value + } + private static void WriteArrayValue(XmlWriter XMLwrt, XmlRpcValueArray val) + { + XMLwrt.WriteStartElement("value");//value + XMLwrt.WriteStartElement("array");//array + XMLwrt.WriteStartElement("data");//data + foreach (IXmlRpcValue o in val.Values) + { + if (o is XmlRpcValueBasic) + { + WriteBasicValue(XMLwrt, (XmlRpcValueBasic)o); + } + else if (o is XmlRpcValueStruct) + { + WriteStructValue(XMLwrt, (XmlRpcValueStruct)o); + } + else if (o is XmlRpcValueArray) + { + WriteArrayValue(XMLwrt, (XmlRpcValueArray)o); + } + } + XMLwrt.WriteEndElement();//data + XMLwrt.WriteEndElement();//array + XMLwrt.WriteEndElement();//value + } + private static IXmlRpcValue ReadValue(XmlReader xmlReader) + { + while (xmlReader.Read()) + { + if (xmlReader.Name == "value" && xmlReader.IsStartElement()) + { + xmlReader.Read(); + if (xmlReader.Name == "string" && xmlReader.IsStartElement()) + { + return new XmlRpcValueBasic(xmlReader.ReadString(), XmlRpcBasicValueType.String); + } + else if (xmlReader.Name == "int" && xmlReader.IsStartElement()) + { + return new XmlRpcValueBasic(int.Parse(xmlReader.ReadString()), XmlRpcBasicValueType.Int); + } + else if (xmlReader.Name == "boolean" && xmlReader.IsStartElement()) + { + return new XmlRpcValueBasic(xmlReader.ReadString() == "1", XmlRpcBasicValueType.Boolean); + } + else if (xmlReader.Name == "double" && xmlReader.IsStartElement()) + { + return new XmlRpcValueBasic(double.Parse(xmlReader.ReadString()), XmlRpcBasicValueType.Double); + } + else if (xmlReader.Name == "dateTime.iso8601" && xmlReader.IsStartElement()) + { + string date = xmlReader.ReadString(); + int year = int.Parse(date.Substring(0, 4)); + int month = int.Parse(date.Substring(4, 2)); + int day = int.Parse(date.Substring(6, 2)); + int hour = int.Parse(date.Substring(9, 2)); + int minute = int.Parse(date.Substring(12, 2));//19980717T14:08:55 + int sec = int.Parse(date.Substring(15, 2)); + DateTime time = new DateTime(year, month, day, hour, minute, sec); + return new XmlRpcValueBasic(time, XmlRpcBasicValueType.dateTime_iso8601); + } + else if (xmlReader.Name == "base64" && xmlReader.IsStartElement()) + { + return new XmlRpcValueBasic(BitConverter.ToInt64(Convert.FromBase64String(xmlReader.ReadString()), 0) + , XmlRpcBasicValueType.Double); + } + else if (xmlReader.Name == "struct" && xmlReader.IsStartElement()) + { + XmlRpcValueStruct strct = new XmlRpcValueStruct(new List()); + // Read members... + while (xmlReader.Read()) + { + if (xmlReader.Name == "member" && xmlReader.IsStartElement()) + { + XmlRpcStructMember member = new XmlRpcStructMember("", null); + xmlReader.Read();// read name + member.Name = xmlReader.ReadString(); + + IXmlRpcValue val = ReadValue(xmlReader); + if (val != null) + { + member.Data = val; + strct.Members.Add(member); + } + } + else if (xmlReader.Name == "struct" && !xmlReader.IsStartElement()) + { + return strct; + } + } + return strct; + } + else if (xmlReader.Name == "array" && xmlReader.IsStartElement()) + { + XmlRpcValueArray array = new XmlRpcValueArray(); + // Read members... + while (xmlReader.Read()) + { + if (xmlReader.Name == "array" && !xmlReader.IsStartElement()) + { + return array; + } + else + { + IXmlRpcValue val = ReadValue(xmlReader); + if (val != null) + array.Values.Add(val); + } + } + return array; + } + } + else break; + } + return null; + } + } +} -- cgit v1.2.3 From 9ef0793b28cfc69ef73838523860a92b8c50b724 Mon Sep 17 00:00:00 2001 From: Luis Miguel Almánzar Date: Mon, 5 May 2014 03:36:26 -0400 Subject: finish implementation of opensubtitles downloader --- .../Providers/ISubtitleProvider.cs | 2 + .../Subtitles/OpenSubtitleDownloader.cs | 47 ++++++++++++++-------- .../SubtitleTypes/SubtitleSearchParameters.cs | 11 +++++ 3 files changed, 44 insertions(+), 16 deletions(-) (limited to 'OpenSubtitlesHandler') diff --git a/MediaBrowser.Controller/Providers/ISubtitleProvider.cs b/MediaBrowser.Controller/Providers/ISubtitleProvider.cs index a3aaaf298e..adefea84d2 100644 --- a/MediaBrowser.Controller/Providers/ISubtitleProvider.cs +++ b/MediaBrowser.Controller/Providers/ISubtitleProvider.cs @@ -49,7 +49,9 @@ namespace MediaBrowser.Controller.Providers public string MediaPath { get; set; } public string SeriesName { get; set; } + public string Name { get; set; } public int? IndexNumber { get; set; } public int? ParentIndexNumber { get; set; } + public long ImdbId { get; set; } } } diff --git a/MediaBrowser.Providers/Subtitles/OpenSubtitleDownloader.cs b/MediaBrowser.Providers/Subtitles/OpenSubtitleDownloader.cs index 12d8b8fc65..6025493e79 100644 --- a/MediaBrowser.Providers/Subtitles/OpenSubtitleDownloader.cs +++ b/MediaBrowser.Providers/Subtitles/OpenSubtitleDownloader.cs @@ -37,25 +37,31 @@ namespace MediaBrowser.Providers.Subtitles public Task GetSubtitles(SubtitleRequest request, CancellationToken cancellationToken) { - return request.ContentType == SubtitleMediaType.Episode - ? GetEpisodeSubtitles(request, cancellationToken) - : GetMovieSubtitles(request, cancellationToken); + return GetMediaSubtitleSubtitles(request, cancellationToken); } - public async Task GetMovieSubtitles(SubtitleRequest request, CancellationToken cancellationToken) - { - throw new NotImplementedException(); - } - - public async Task GetEpisodeSubtitles(SubtitleRequest request, CancellationToken cancellationToken) + public async Task GetMediaSubtitleSubtitles(SubtitleRequest request, CancellationToken cancellationToken) { var response = new SubtitleResponse(); - if (!request.IndexNumber.HasValue || !request.ParentIndexNumber.HasValue) + switch (request.ContentType) { - _logger.Debug("Information Missing"); - return response; + case SubtitleMediaType.Episode: + if (!request.IndexNumber.HasValue || !request.ParentIndexNumber.HasValue || string.IsNullOrEmpty(request.SeriesName)) + { + _logger.Debug("Information Missing"); + return response; + } + break; + case SubtitleMediaType.Movie: + if (string.IsNullOrEmpty(request.Name)) + { + _logger.Debug("Information Missing"); + return response; + } + break; } + if (string.IsNullOrEmpty(request.MediaPath)) { _logger.Debug("Path Missing"); @@ -76,10 +82,13 @@ namespace MediaBrowser.Providers.Subtitles var fileInfo = new FileInfo(request.MediaPath); var movieByteSize = fileInfo.Length; + var subtitleSearchParameters = request.ContentType == SubtitleMediaType.Episode + ? new SubtitleSearchParameters(subLanguageId, request.SeriesName, request.ParentIndexNumber.Value.ToString(_usCulture), request.IndexNumber.Value.ToString(_usCulture)) + : new SubtitleSearchParameters(subLanguageId, request.Name); + var parms = new List { new SubtitleSearchParameters(subLanguageId, hash, movieByteSize), - new SubtitleSearchParameters(subLanguageId, request.SeriesName, request.ParentIndexNumber.Value.ToString(_usCulture), request.IndexNumber.Value.ToString(_usCulture)), - + subtitleSearchParameters }; var result = OpenSubtitles.SearchSubtitles(parms.ToArray()); @@ -89,8 +98,14 @@ namespace MediaBrowser.Providers.Subtitles return null; } + Predicate mediaFilter = + x => + request.ContentType == SubtitleMediaType.Episode + ? int.Parse(x.SeriesSeason) == request.ParentIndexNumber && int.Parse(x.SeriesEpisode) == request.IndexNumber + : long.Parse(x.IDMovieImdb) == request.ImdbId; + var results = ((MethodResponseSubtitleSearch)result).Results; - var bestResult = results.Where(x => x.SubBad == "0" && int.Parse(x.SeriesSeason) == request.ParentIndexNumber && int.Parse(x.SeriesEpisode) == request.IndexNumber) + var bestResult = results.Where(x => x.SubBad == "0" && mediaFilter(x)) .OrderBy(x => x.MovieHash == hash) .ThenBy(x => Math.Abs(long.Parse(x.MovieByteSize) - movieByteSize)) .ThenByDescending(x => int.Parse(x.SubDownloadsCnt)) @@ -124,7 +139,7 @@ namespace MediaBrowser.Providers.Subtitles var data = Convert.FromBase64String(res.Data); response.HasContent = true; - response.Format = SubtitleFormat.SRT; + response.Format = subtitle.SubFormat.ToUpper(); response.Stream = new MemoryStream(Utilities.Decompress(new MemoryStream(data))); return response; } diff --git a/OpenSubtitlesHandler/SubtitleTypes/SubtitleSearchParameters.cs b/OpenSubtitlesHandler/SubtitleTypes/SubtitleSearchParameters.cs index e11ff1b9a9..f2e70ef813 100644 --- a/OpenSubtitlesHandler/SubtitleTypes/SubtitleSearchParameters.cs +++ b/OpenSubtitlesHandler/SubtitleTypes/SubtitleSearchParameters.cs @@ -42,6 +42,17 @@ namespace OpenSubtitlesHandler this._query = ""; } + public SubtitleSearchParameters(string subLanguageId, string query) + { + this.subLanguageId = subLanguageId; + this.movieHash = ""; + this.movieByteSize = 0; + this.imdbid = ""; + this._episode = ""; + this._season = ""; + this._query = query; + } + public SubtitleSearchParameters(string subLanguageId, string query, string season, string episode) { this.subLanguageId = subLanguageId; -- cgit v1.2.3 From 0d025f7fb620bf2a24ca9aa4e5994f132e02e7c0 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 6 May 2014 22:28:19 -0400 Subject: beginning remote subtitle downloading --- MediaBrowser.Api/ChannelService.cs | 2 +- MediaBrowser.Api/Images/ImageByNameService.cs | 90 ++++++++++++- MediaBrowser.Api/Images/ImageService.cs | 4 +- MediaBrowser.Api/ItemLookupService.cs | 27 +++- MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs | 2 +- MediaBrowser.Api/UserLibrary/ItemsService.cs | 3 + .../HttpClientManager/HttpClientManager.cs | 13 +- .../ScheduledTasks/Tasks/DeleteCacheFileTask.cs | 6 +- MediaBrowser.Common/Net/HttpRequestOptions.cs | 16 +-- .../MediaBrowser.Controller.csproj | 3 +- .../Providers/ISubtitleProvider.cs | 66 ---------- .../Subtitles/ISubtitleManager.cs | 50 ++++++++ .../Subtitles/ISubtitleProvider.cs | 75 +++++++++++ MediaBrowser.Dlna/PlayTo/Device.cs | 4 + MediaBrowser.Dlna/PlayTo/DlnaController.cs | 31 +++++ .../MediaBrowser.MediaEncoding.csproj | 2 +- .../Subtitles/ISubtitleParser.cs | 2 +- MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs | 4 +- MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs | 4 +- .../Subtitles/SubtitleInfo.cs | 22 ---- .../Subtitles/SubtitleTrackInfo.cs | 22 ++++ .../MediaBrowser.Model.Portable.csproj | 3 + .../MediaBrowser.Model.net35.csproj | 3 + MediaBrowser.Model/ApiClient/IApiClient.cs | 2 +- .../Configuration/ServerConfiguration.cs | 17 +++ MediaBrowser.Model/Dto/StreamOptions.cs | 2 +- MediaBrowser.Model/MediaBrowser.Model.csproj | 1 + MediaBrowser.Model/Providers/RemoteSubtitleInfo.cs | 19 +++ MediaBrowser.Model/Querying/ItemFilter.cs | 4 + .../MediaBrowser.Providers.csproj | 2 + .../MediaInfo/FFProbeProvider.cs | 14 +- .../MediaInfo/FFProbeVideoInfo.cs | 57 +++++++-- .../MediaInfo/SubtitleDownloader.cs | 140 ++++++++++++++++++++ .../Subtitles/OpenSubtitleDownloader.cs | 138 +++++++++++--------- .../Subtitles/SubtitleManager.cs | 141 +++++++++++++++++++++ .../Channels/ChannelManager.cs | 2 +- .../Collections/CollectionManager.cs | 8 +- .../HttpServer/ServerLogger.cs | 6 +- .../Library/Validators/PeoplePostScanTask.cs | 44 ------- .../Localization/LocalizationManager.cs | 47 ++----- .../Localization/Server/server.json | 81 +++++++++++- .../Localization/countries.json | 1 + .../Localization/cultures.json | 1 + .../MediaBrowser.Server.Implementations.csproj | 3 +- MediaBrowser.ServerApplication/ApplicationHost.cs | 11 +- .../FFMpeg/FFMpegDownloadInfo.cs | 81 +++++++----- .../FFMpeg/FFMpegDownloader.cs | 135 +++++++++++--------- .../FFMpeg/FFMpegInfo.cs | 2 +- .../MediaBrowser.WebDashboard.csproj | 3 + OpenSubtitlesHandler/OpenSubtitles.cs | 52 ++++++++ OpenSubtitlesHandler/Utilities.cs | 20 ++- 51 files changed, 1112 insertions(+), 376 deletions(-) delete mode 100644 MediaBrowser.Controller/Providers/ISubtitleProvider.cs create mode 100644 MediaBrowser.Controller/Subtitles/ISubtitleManager.cs create mode 100644 MediaBrowser.Controller/Subtitles/ISubtitleProvider.cs delete mode 100644 MediaBrowser.MediaEncoding/Subtitles/SubtitleInfo.cs create mode 100644 MediaBrowser.MediaEncoding/Subtitles/SubtitleTrackInfo.cs create mode 100644 MediaBrowser.Model/Providers/RemoteSubtitleInfo.cs create mode 100644 MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs create mode 100644 MediaBrowser.Providers/Subtitles/SubtitleManager.cs delete mode 100644 MediaBrowser.Server.Implementations/Library/Validators/PeoplePostScanTask.cs create mode 100644 MediaBrowser.Server.Implementations/Localization/countries.json create mode 100644 MediaBrowser.Server.Implementations/Localization/cultures.json (limited to 'OpenSubtitlesHandler') diff --git a/MediaBrowser.Api/ChannelService.cs b/MediaBrowser.Api/ChannelService.cs index a0795c14d4..9fa4eec4ae 100644 --- a/MediaBrowser.Api/ChannelService.cs +++ b/MediaBrowser.Api/ChannelService.cs @@ -67,7 +67,7 @@ namespace MediaBrowser.Api [ApiMember(Name = "SortOrder", Description = "Sort Order - Ascending,Descending", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] public SortOrder? SortOrder { get; set; } - [ApiMember(Name = "Filters", Description = "Optional. Specify additional filters to apply. This allows multiple, comma delimeted. Options: IsFolder, IsNotFolder, IsUnplayed, IsPlayed, IsFavorite, IsRecentlyAdded, IsResumable, Likes, Dislikes", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] + [ApiMember(Name = "Filters", Description = "Optional. Specify additional filters to apply. This allows multiple, comma delimeted. Options: IsFolder, IsNotFolder, IsUnplayed, IsPlayed, IsFavorite, IsResumable, Likes, Dislikes", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string Filters { get; set; } [ApiMember(Name = "SortBy", Description = "Optional. Specify one or more sort orders, comma delimeted. Options: Album, AlbumArtist, Artist, Budget, CommunityRating, CriticRating, DateCreated, DatePlayed, PlayCount, PremiereDate, ProductionYear, SortName, Random, Revenue, Runtime", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] diff --git a/MediaBrowser.Api/Images/ImageByNameService.cs b/MediaBrowser.Api/Images/ImageByNameService.cs index 44a69f6dea..6dda2ae7af 100644 --- a/MediaBrowser.Api/Images/ImageByNameService.cs +++ b/MediaBrowser.Api/Images/ImageByNameService.cs @@ -1,8 +1,10 @@ using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.IO; using MediaBrowser.Controller; using MediaBrowser.Controller.Entities; using ServiceStack; using System; +using System.Collections.Generic; using System.IO; using System.Linq; @@ -70,6 +72,32 @@ namespace MediaBrowser.Api.Images public string Theme { get; set; } } + [Route("/Images/MediaInfo", "GET")] + [Api(Description = "Gets all media info image by name")] + public class GetMediaInfoImages : IReturn> + { + } + + [Route("/Images/Ratings", "GET")] + [Api(Description = "Gets all rating images by name")] + public class GetRatingImages : IReturn> + { + } + + [Route("/Images/General", "GET")] + [Api(Description = "Gets all general images by name")] + public class GetGeneralImages : IReturn> + { + } + + public class ImageByNameInfo + { + public string Name { get; set; } + public string Theme { get; set; } + public long FileLength { get; set; } + public string Format { get; set; } + } + /// /// Class ImageByNameService /// @@ -89,6 +117,60 @@ namespace MediaBrowser.Api.Images _appPaths = appPaths; } + public object Get(GetMediaInfoImages request) + { + return ToOptimizedResult(GetImageList(_appPaths.MediaInfoImagesPath)); + } + + public object Get(GetRatingImages request) + { + return ToOptimizedResult(GetImageList(_appPaths.RatingsPath)); + } + + public object Get(GetGeneralImages request) + { + return ToOptimizedResult(GetImageList(_appPaths.GeneralPath)); + } + + private List GetImageList(string path) + { + try + { + return new DirectoryInfo(path) + .GetFiles("*", SearchOption.AllDirectories) + .Where(i => BaseItem.SupportedImageExtensions.Contains(i.Extension, StringComparer.Ordinal)) + .Select(i => new ImageByNameInfo + { + Name = Path.GetFileNameWithoutExtension(i.FullName), + FileLength = i.Length, + Theme = GetThemeName(i.FullName, path), + Format = i.Extension.ToLower().TrimStart('.') + }) + .OrderBy(i => i.Name) + .ToList(); + } + catch (DirectoryNotFoundException) + { + return new List(); + } + } + + private string GetThemeName(string path, string rootImagePath) + { + var parentName = Path.GetDirectoryName(path); + + if (string.Equals(parentName, rootImagePath, StringComparison.OrdinalIgnoreCase)) + { + return null; + } + + parentName = Path.GetFileName(parentName); + + return string.Equals(parentName, "all", StringComparison.OrdinalIgnoreCase) ? + null : + parentName; + } + /// /// Gets the specified request. /// @@ -118,7 +200,8 @@ namespace MediaBrowser.Api.Images if (Directory.Exists(themeFolder)) { - var path = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(themeFolder, request.Name + i)) + var path = BaseItem.SupportedImageExtensions + .Select(i => Path.Combine(themeFolder, request.Name + i)) .FirstOrDefault(File.Exists); if (!string.IsNullOrEmpty(path)) @@ -134,7 +217,8 @@ namespace MediaBrowser.Api.Images // Avoid implicitly captured closure var currentRequest = request; - var path = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(allFolder, currentRequest.Name + i)) + var path = BaseItem.SupportedImageExtensions + .Select(i => Path.Combine(allFolder, currentRequest.Name + i)) .FirstOrDefault(File.Exists); if (!string.IsNullOrEmpty(path)) @@ -175,7 +259,7 @@ namespace MediaBrowser.Api.Images var path = BaseItem.SupportedImageExtensions.Select(i => Path.Combine(allFolder, currentRequest.Name + i)) .FirstOrDefault(File.Exists); - + if (!string.IsNullOrEmpty(path)) { return ToStaticFileResult(path); diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index a5bb291ae9..ce3eaf053d 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -1,5 +1,4 @@ -using System.Globalization; -using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; @@ -14,6 +13,7 @@ using ServiceStack.Text.Controller; using ServiceStack.Web; using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading; diff --git a/MediaBrowser.Api/ItemLookupService.cs b/MediaBrowser.Api/ItemLookupService.cs index b600c3b464..86fdd6da87 100644 --- a/MediaBrowser.Api/ItemLookupService.cs +++ b/MediaBrowser.Api/ItemLookupService.cs @@ -1,13 +1,13 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Controller; -using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; +using MediaBrowser.Controller.Subtitles; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; using ServiceStack; @@ -32,6 +32,16 @@ namespace MediaBrowser.Api public string Id { get; set; } } + [Route("/Items/{Id}/RemoteSearch/Subtitles/{Language}", "GET")] + public class SearchRemoteSubtitles : IReturn> + { + [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string Id { get; set; } + + [ApiMember(Name = "Language", Description = "Language", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string Language { get; set; } + } + [Route("/Items/RemoteSearch/Movie", "POST")] [Api(Description = "Gets external id infos for an item")] public class GetMovieRemoteSearchResults : RemoteSearchQuery, IReturn> @@ -107,19 +117,28 @@ namespace MediaBrowser.Api public class ItemLookupService : BaseApiService { - private readonly IDtoService _dtoService; private readonly IProviderManager _providerManager; private readonly IServerApplicationPaths _appPaths; private readonly IFileSystem _fileSystem; private readonly ILibraryManager _libraryManager; + private readonly ISubtitleManager _subtitleManager; - public ItemLookupService(IDtoService dtoService, IProviderManager providerManager, IServerApplicationPaths appPaths, IFileSystem fileSystem, ILibraryManager libraryManager) + public ItemLookupService(IProviderManager providerManager, IServerApplicationPaths appPaths, IFileSystem fileSystem, ILibraryManager libraryManager, ISubtitleManager subtitleManager) { - _dtoService = dtoService; _providerManager = providerManager; _appPaths = appPaths; _fileSystem = fileSystem; _libraryManager = libraryManager; + _subtitleManager = subtitleManager; + } + + public object Get(SearchRemoteSubtitles request) + { + var video = (Video)_libraryManager.GetItemById(request.Id); + + var response = _subtitleManager.SearchSubtitles(video, request.Language, CancellationToken.None).Result; + + return ToOptimizedResult(response); } public object Get(GetExternalIdInfos request) diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs index f1fe904f36..cc76ee95fd 100644 --- a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs +++ b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs @@ -69,7 +69,7 @@ namespace MediaBrowser.Api.UserLibrary /// Filters to apply to the results /// /// The filters. - [ApiMember(Name = "Filters", Description = "Optional. Specify additional filters to apply. This allows multiple, comma delimeted. Options: IsFolder, IsNotFolder, IsUnplayed, IsPlayed, IsFavorite, IsRecentlyAdded, IsResumable, Likes, Dislikes", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] + [ApiMember(Name = "Filters", Description = "Optional. Specify additional filters to apply. This allows multiple, comma delimeted. Options: IsFolder, IsNotFolder, IsUnplayed, IsPlayed, IsFavorite, IsResumable, Likes, Dislikes", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string Filters { get; set; } /// diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index 1cd8191970..c7f36e6ac9 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -521,6 +521,9 @@ namespace MediaBrowser.Api.UserLibrary case ItemFilter.IsNotFolder: return items.Where(item => !item.IsFolder); + + case ItemFilter.IsRecentlyAdded: + return items.Where(item => (DateTime.UtcNow - item.DateCreated).TotalDays <= 10); } return items; diff --git a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs index a88f457c5e..0a9f0ff8a6 100644 --- a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs +++ b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs @@ -114,9 +114,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager request.AutomaticDecompression = enableHttpCompression ? DecompressionMethods.Deflate : DecompressionMethods.None; - request.CachePolicy = options.CachePolicy == Net.HttpRequestCachePolicy.None ? - new RequestCachePolicy(RequestCacheLevel.BypassCache) : - new RequestCachePolicy(RequestCacheLevel.Revalidate); + request.CachePolicy = new RequestCachePolicy(RequestCacheLevel.BypassCache); request.ConnectionGroupName = GetHostFromUrl(options.Url); request.KeepAlive = true; @@ -124,6 +122,11 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager request.Pipelined = true; request.Timeout = 20000; + if (!string.IsNullOrEmpty(options.Host)) + { + request.Host = options.Host; + } + #if !__MonoCS__ // This is a hack to prevent KeepAlive from getting disabled internally by the HttpWebRequest // May need to remove this for mono @@ -234,9 +237,11 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager !string.IsNullOrEmpty(options.RequestContent) || string.Equals(httpMethod, "post", StringComparison.OrdinalIgnoreCase)) { - var bytes = options.RequestContentBytes ?? Encoding.UTF8.GetBytes(options.RequestContent ?? string.Empty); + var bytes = options.RequestContentBytes ?? + Encoding.UTF8.GetBytes(options.RequestContent ?? string.Empty); httpWebRequest.ContentType = options.RequestContentType ?? "application/x-www-form-urlencoded"; + httpWebRequest.ContentLength = bytes.Length; httpWebRequest.GetRequestStream().Write(bytes, 0, bytes.Length); } diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs index a5b8de5541..dbeedfed55 100644 --- a/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs +++ b/MediaBrowser.Common.Implementations/ScheduledTasks/Tasks/DeleteCacheFileTask.cs @@ -25,7 +25,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks private readonly ILogger _logger; private readonly IFileSystem _fileSystem; - + /// /// Initializes a new instance of the class. /// @@ -74,9 +74,11 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks progress.Report(90); + minDateModified = DateTime.UtcNow.AddDays(-3); + try { - DeleteCacheFilesFromDirectory(cancellationToken, ApplicationPaths.TempDirectory, DateTime.MaxValue, progress); + DeleteCacheFilesFromDirectory(cancellationToken, ApplicationPaths.TempDirectory, minDateModified, progress); } catch (DirectoryNotFoundException) { diff --git a/MediaBrowser.Common/Net/HttpRequestOptions.cs b/MediaBrowser.Common/Net/HttpRequestOptions.cs index 11152b30fc..192264eed1 100644 --- a/MediaBrowser.Common/Net/HttpRequestOptions.cs +++ b/MediaBrowser.Common/Net/HttpRequestOptions.cs @@ -52,6 +52,12 @@ namespace MediaBrowser.Common.Net } } + /// + /// Gets or sets the host. + /// + /// The host. + public string Host { get; set; } + /// /// Gets or sets the progress. /// @@ -76,8 +82,6 @@ namespace MediaBrowser.Common.Net public bool LogRequest { get; set; } public bool LogErrorResponseBody { get; set; } - - public HttpRequestCachePolicy CachePolicy { get; set; } private string GetHeaderValue(string name) { @@ -96,17 +100,9 @@ namespace MediaBrowser.Common.Net EnableHttpCompression = true; BufferContent = true; - CachePolicy = HttpRequestCachePolicy.None; - RequestHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); LogRequest = true; } } - - public enum HttpRequestCachePolicy - { - None = 1, - Validate = 2 - } } diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 5e259359ff..6a3709dda6 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -191,7 +191,8 @@ - + + diff --git a/MediaBrowser.Controller/Providers/ISubtitleProvider.cs b/MediaBrowser.Controller/Providers/ISubtitleProvider.cs deleted file mode 100644 index 09ca27e307..0000000000 --- a/MediaBrowser.Controller/Providers/ISubtitleProvider.cs +++ /dev/null @@ -1,66 +0,0 @@ -using MediaBrowser.Model.Entities; -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Providers -{ - public interface ISubtitleProvider - { - /// - /// Gets the name. - /// - /// The name. - string Name { get; } - - /// - /// Gets the supported media types. - /// - /// The supported media types. - IEnumerable SupportedMediaTypes { get; } - - /// - /// Gets the subtitles. - /// - /// The request. - /// The cancellation token. - /// Task{SubtitleResponse}. - Task GetSubtitles(SubtitleRequest request, CancellationToken cancellationToken); - } - - public enum SubtitleMediaType - { - Episode = 0, - Movie = 1 - } - - public class SubtitleResponse - { - public string Format { get; set; } - public bool HasContent { get; set; } - public Stream Stream { get; set; } - } - - public class SubtitleRequest : IHasProviderIds - { - public string Language { get; set; } - - public SubtitleMediaType ContentType { get; set; } - - public string MediaPath { get; set; } - public string SeriesName { get; set; } - public string Name { get; set; } - public int? IndexNumber { get; set; } - public int? IndexNumberEnd { get; set; } - public int? ParentIndexNumber { get; set; } - public int? ProductionYear { get; set; } - public Dictionary ProviderIds { get; set; } - - public SubtitleRequest() - { - ProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase); - } - } -} diff --git a/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs b/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs new file mode 100644 index 0000000000..8b0ef223c3 --- /dev/null +++ b/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs @@ -0,0 +1,50 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Providers; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Subtitles +{ + public interface ISubtitleManager + { + /// + /// Adds the parts. + /// + /// The subtitle providers. + void AddParts(IEnumerable subtitleProviders); + + /// + /// Searches the subtitles. + /// + /// The video. + /// The language. + /// The cancellation token. + /// Task{IEnumerable{RemoteSubtitleInfo}}. + Task> SearchSubtitles(Video video, + string language, + CancellationToken cancellationToken); + + /// + /// Searches the subtitles. + /// + /// The request. + /// The cancellation token. + /// Task{IEnumerable{RemoteSubtitleInfo}}. + Task> SearchSubtitles(SubtitleSearchRequest request, + CancellationToken cancellationToken); + + /// + /// Downloads the subtitles. + /// + /// The video. + /// The subtitle identifier. + /// Name of the provider. + /// The cancellation token. + /// Task. + Task DownloadSubtitles(Video video, + string subtitleId, + string providerName, + CancellationToken cancellationToken); + } +} diff --git a/MediaBrowser.Controller/Subtitles/ISubtitleProvider.cs b/MediaBrowser.Controller/Subtitles/ISubtitleProvider.cs new file mode 100644 index 0000000000..1409b7d503 --- /dev/null +++ b/MediaBrowser.Controller/Subtitles/ISubtitleProvider.cs @@ -0,0 +1,75 @@ +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Subtitles +{ + public interface ISubtitleProvider + { + /// + /// Gets the name. + /// + /// The name. + string Name { get; } + + /// + /// Gets the supported media types. + /// + /// The supported media types. + IEnumerable SupportedMediaTypes { get; } + + /// + /// Searches the subtitles. + /// + /// The request. + /// The cancellation token. + /// Task{IEnumerable{RemoteSubtitleInfo}}. + Task> SearchSubtitles(SubtitleSearchRequest request, CancellationToken cancellationToken); + + /// + /// Gets the subtitles. + /// + /// The identifier. + /// The cancellation token. + /// Task{SubtitleResponse}. + Task GetSubtitles(string id, CancellationToken cancellationToken); + } + + public enum SubtitleMediaType + { + Episode = 0, + Movie = 1 + } + + public class SubtitleResponse + { + public string Language { get; set; } + public string Format { get; set; } + public Stream Stream { get; set; } + } + + public class SubtitleSearchRequest : IHasProviderIds + { + public string Language { get; set; } + + public SubtitleMediaType ContentType { get; set; } + + public string MediaPath { get; set; } + public string SeriesName { get; set; } + public string Name { get; set; } + public int? IndexNumber { get; set; } + public int? IndexNumberEnd { get; set; } + public int? ParentIndexNumber { get; set; } + public int? ProductionYear { get; set; } + public Dictionary ProviderIds { get; set; } + + public SubtitleSearchRequest() + { + ProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + } +} diff --git a/MediaBrowser.Dlna/PlayTo/Device.cs b/MediaBrowser.Dlna/PlayTo/Device.cs index 1c7ed13b67..3e5e877cdc 100644 --- a/MediaBrowser.Dlna/PlayTo/Device.cs +++ b/MediaBrowser.Dlna/PlayTo/Device.cs @@ -77,6 +77,8 @@ namespace MediaBrowser.Dlna.PlayTo private readonly ILogger _logger; private readonly IServerConfigurationManager _config; + public DateTime DateLastActivity { get; private set; } + public Device(DeviceInfo deviceProperties, IHttpClient httpClient, ILogger logger, IServerConfigurationManager config) { Properties = deviceProperties; @@ -386,6 +388,8 @@ namespace MediaBrowser.Dlna.PlayTo { var transportState = await GetTransportInfo().ConfigureAwait(false); + DateLastActivity = DateTime.UtcNow; + if (transportState.HasValue) { // If we're not playing anything no need to get additional data diff --git a/MediaBrowser.Dlna/PlayTo/DlnaController.cs b/MediaBrowser.Dlna/PlayTo/DlnaController.cs index fb5e0bf34f..673a7c2459 100644 --- a/MediaBrowser.Dlna/PlayTo/DlnaController.cs +++ b/MediaBrowser.Dlna/PlayTo/DlnaController.cs @@ -51,6 +51,8 @@ namespace MediaBrowser.Dlna.PlayTo } } + private Timer _updateTimer; + public PlayToController(SessionInfo session, ISessionManager sessionManager, IItemRepository itemRepository, ILibraryManager libraryManager, ILogger logger, IDlnaManager dlnaManager, IUserManager userManager, IDtoService dtoService, IImageProcessor imageProcessor, SsdpHandler ssdpHandler, string serverAddress) { _session = session; @@ -75,6 +77,24 @@ namespace MediaBrowser.Dlna.PlayTo _device.Start(); _ssdpHandler.MessageReceived += _SsdpHandler_MessageReceived; + + _updateTimer = new Timer(updateTimer_Elapsed, null, 60000, 60000); + } + + private async void updateTimer_Elapsed(object state) + { + if (DateTime.UtcNow >= _device.DateLastActivity.AddSeconds(60)) + { + try + { + // Session is inactive, mark it for Disposal and don't start the elapsed timer. + await _sessionManager.ReportSessionEnded(_session.Id).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error in ReportSessionEnded", ex); + } + } } private string GetServerAddress() @@ -571,10 +591,21 @@ namespace MediaBrowser.Dlna.PlayTo _device.PlaybackStopped -= _device_PlaybackStopped; _ssdpHandler.MessageReceived -= _SsdpHandler_MessageReceived; + DisposeUpdateTimer(); + _device.Dispose(); } } + private void DisposeUpdateTimer() + { + if (_updateTimer != null) + { + _updateTimer.Dispose(); + _updateTimer = null; + } + } + private readonly CultureInfo _usCulture = new CultureInfo("en-US"); public Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken) diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index 291bb0222f..19287b0cbb 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -64,7 +64,7 @@ - + diff --git a/MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs b/MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs index 5e7ad6699a..b983bc5d4a 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/ISubtitleParser.cs @@ -4,6 +4,6 @@ namespace MediaBrowser.MediaEncoding.Subtitles { public interface ISubtitleParser { - SubtitleInfo Parse(Stream stream); + SubtitleTrackInfo Parse(Stream stream); } } diff --git a/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs index af0009a82e..410c0bbdd6 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs @@ -7,9 +7,9 @@ using System.Threading.Tasks; namespace MediaBrowser.MediaEncoding.Subtitles { - public class SrtParser + public class SrtParser : ISubtitleParser { - public SubtitleInfo Parse(Stream stream) + public SubtitleTrackInfo Parse(Stream stream) { throw new NotImplementedException(); } diff --git a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs index e134416b13..ca7e58371c 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs @@ -7,9 +7,9 @@ using System.Threading.Tasks; namespace MediaBrowser.MediaEncoding.Subtitles { - public class SsaParser + public class SsaParser : ISubtitleParser { - public SubtitleInfo Parse(Stream stream) + public SubtitleTrackInfo Parse(Stream stream) { throw new NotImplementedException(); } diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleInfo.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleInfo.cs deleted file mode 100644 index 812b0c7d4c..0000000000 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleInfo.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Collections.Generic; - -namespace MediaBrowser.MediaEncoding.Subtitles -{ - public class SubtitleInfo - { - public List TrackEvents { get; set; } - - public SubtitleInfo() - { - TrackEvents = new List(); - } - } - - public class SubtitleTrackEvent - { - public string Id { get; set; } - public string Text { get; set; } - public long StartPositionTicks { get; set; } - public long EndPositionTicks { get; set; } - } -} diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleTrackInfo.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleTrackInfo.cs new file mode 100644 index 0000000000..67d70ed6eb --- /dev/null +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleTrackInfo.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; + +namespace MediaBrowser.MediaEncoding.Subtitles +{ + public class SubtitleTrackInfo + { + public List TrackEvents { get; set; } + + public SubtitleTrackInfo() + { + TrackEvents = new List(); + } + } + + public class SubtitleTrackEvent + { + public string Id { get; set; } + public string Text { get; set; } + public long StartPositionTicks { get; set; } + public long EndPositionTicks { get; set; } + } +} diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj index e8a802725a..991fe3b2a2 100644 --- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj +++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj @@ -416,6 +416,9 @@ Providers\RemoteSearchResult.cs + + Providers\RemoteSubtitleInfo.cs + Querying\ArtistsQuery.cs diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj index 5fb5fae748..771e739bc6 100644 --- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj +++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj @@ -403,6 +403,9 @@ Providers\RemoteSearchResult.cs + + Providers\RemoteSubtitleInfo.cs + Querying\ArtistsQuery.cs diff --git a/MediaBrowser.Model/ApiClient/IApiClient.cs b/MediaBrowser.Model/ApiClient/IApiClient.cs index dd1603d01a..c9f5f3ae79 100644 --- a/MediaBrowser.Model/ApiClient/IApiClient.cs +++ b/MediaBrowser.Model/ApiClient/IApiClient.cs @@ -760,7 +760,7 @@ namespace MediaBrowser.Model.ApiClient /// /// The options. /// System.String. - string GetSubtitleUrl(SubtitleOptions options); + string GetSubtitleUrl(SubtitleDownloadOptions options); /// /// Gets an image url that can be used to download an image from the api diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 0fb9db6c04..486268e2b6 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -221,6 +221,8 @@ namespace MediaBrowser.Model.Configuration public NotificationOptions NotificationOptions { get; set; } + public SubtitleOptions SubtitleOptions { get; set; } + /// /// Initializes a new instance of the class. /// @@ -284,6 +286,8 @@ namespace MediaBrowser.Model.Configuration UICulture = "en-us"; NotificationOptions = new NotificationOptions(); + + SubtitleOptions = new SubtitleOptions(); } } @@ -311,4 +315,17 @@ namespace MediaBrowser.Model.Configuration public string From { get; set; } public string To { get; set; } } + + public class SubtitleOptions + { + public bool RequireExternalSubtitles { get; set; } + public string[] SubtitleDownloadLanguages { get; set; } + public bool DownloadMovieSubtitles { get; set; } + public bool DownloadEpisodeSubtitles { get; set; } + + public SubtitleOptions() + { + SubtitleDownloadLanguages = new string[] { }; + } + } } diff --git a/MediaBrowser.Model/Dto/StreamOptions.cs b/MediaBrowser.Model/Dto/StreamOptions.cs index b1ead2ca38..861fa4e01e 100644 --- a/MediaBrowser.Model/Dto/StreamOptions.cs +++ b/MediaBrowser.Model/Dto/StreamOptions.cs @@ -159,7 +159,7 @@ public string DeviceId { get; set; } } - public class SubtitleOptions + public class SubtitleDownloadOptions { /// /// Gets or sets the item identifier. diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 877eb54447..aaa29d21c0 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -139,6 +139,7 @@ + diff --git a/MediaBrowser.Model/Providers/RemoteSubtitleInfo.cs b/MediaBrowser.Model/Providers/RemoteSubtitleInfo.cs new file mode 100644 index 0000000000..dab9a57a85 --- /dev/null +++ b/MediaBrowser.Model/Providers/RemoteSubtitleInfo.cs @@ -0,0 +1,19 @@ +using System; + +namespace MediaBrowser.Model.Providers +{ + public class RemoteSubtitleInfo + { + public string Language { get; set; } + public string Id { get; set; } + public string ProviderName { get; set; } + public string Name { get; set; } + public string Format { get; set; } + public string Author { get; set; } + public string Comment { get; set; } + public DateTime? DateCreated { get; set; } + public float? CommunityRating { get; set; } + public int? DownloadCount { get; set; } + public bool? IsHashMatch { get; set; } + } +} diff --git a/MediaBrowser.Model/Querying/ItemFilter.cs b/MediaBrowser.Model/Querying/ItemFilter.cs index 2e88a98c9c..d30978ebfc 100644 --- a/MediaBrowser.Model/Querying/ItemFilter.cs +++ b/MediaBrowser.Model/Querying/ItemFilter.cs @@ -27,6 +27,10 @@ namespace MediaBrowser.Model.Querying /// IsFavorite = 5, /// + /// The is recently added + /// + IsRecentlyAdded = 6, + /// /// The item is resumable /// IsResumable = 7, diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index b19966718a..43402123cb 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -108,6 +108,7 @@ + @@ -187,6 +188,7 @@ + diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs index 7a71a75515..3c03e11b3c 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs @@ -1,5 +1,6 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Movies; @@ -10,15 +11,16 @@ using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Providers; +using MediaBrowser.Controller.Subtitles; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Serialization; using System; +using System.Linq; using System.Threading; using System.Threading.Tasks; -using System.Linq; namespace MediaBrowser.Providers.MediaInfo { @@ -45,6 +47,8 @@ namespace MediaBrowser.Providers.MediaInfo private readonly IJsonSerializer _json; private readonly IEncodingManager _encodingManager; private readonly IFileSystem _fileSystem; + private readonly IServerConfigurationManager _config; + private readonly ISubtitleManager _subtitleManager; public string Name { @@ -96,7 +100,7 @@ namespace MediaBrowser.Providers.MediaInfo return FetchAudioInfo(item, cancellationToken); } - public FFProbeProvider(ILogger logger, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IBlurayExaminer blurayExaminer, ILocalizationManager localization, IApplicationPaths appPaths, IJsonSerializer json, IEncodingManager encodingManager, IFileSystem fileSystem) + public FFProbeProvider(ILogger logger, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IBlurayExaminer blurayExaminer, ILocalizationManager localization, IApplicationPaths appPaths, IJsonSerializer json, IEncodingManager encodingManager, IFileSystem fileSystem, IServerConfigurationManager config, ISubtitleManager subtitleManager) { _logger = logger; _isoManager = isoManager; @@ -108,6 +112,8 @@ namespace MediaBrowser.Providers.MediaInfo _json = json; _encodingManager = encodingManager; _fileSystem = fileSystem; + _config = config; + _subtitleManager = subtitleManager; } private readonly Task _cachedTask = Task.FromResult(ItemUpdateType.None); @@ -134,7 +140,7 @@ namespace MediaBrowser.Providers.MediaInfo return _cachedTask; } - var prober = new FFProbeVideoInfo(_logger, _isoManager, _mediaEncoder, _itemRepo, _blurayExaminer, _localization, _appPaths, _json, _encodingManager, _fileSystem); + var prober = new FFProbeVideoInfo(_logger, _isoManager, _mediaEncoder, _itemRepo, _blurayExaminer, _localization, _appPaths, _json, _encodingManager, _fileSystem, _config, _subtitleManager); return prober.ProbeVideo(item, directoryService, cancellationToken); } @@ -165,7 +171,7 @@ namespace MediaBrowser.Providers.MediaInfo if (video != null && !video.IsPlaceHolder) { - var prober = new FFProbeVideoInfo(_logger, _isoManager, _mediaEncoder, _itemRepo, _blurayExaminer, _localization, _appPaths, _json, _encodingManager, _fileSystem); + var prober = new FFProbeVideoInfo(_logger, _isoManager, _mediaEncoder, _itemRepo, _blurayExaminer, _localization, _appPaths, _json, _encodingManager, _fileSystem, _config, _subtitleManager); return !video.SubtitleFiles.SequenceEqual(prober.GetSubtitleFiles(video, directoryService).Select(i => i.FullName).OrderBy(i => i), StringComparer.OrdinalIgnoreCase); } diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index a7d4a480e3..a2897ef9c2 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -2,12 +2,16 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Providers; +using MediaBrowser.Controller.Subtitles; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; @@ -35,10 +39,12 @@ namespace MediaBrowser.Providers.MediaInfo private readonly IJsonSerializer _json; private readonly IEncodingManager _encodingManager; private readonly IFileSystem _fileSystem; + private readonly IServerConfigurationManager _config; + private readonly ISubtitleManager _subtitleManager; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - public FFProbeVideoInfo(ILogger logger, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IBlurayExaminer blurayExaminer, ILocalizationManager localization, IApplicationPaths appPaths, IJsonSerializer json, IEncodingManager encodingManager, IFileSystem fileSystem) + public FFProbeVideoInfo(ILogger logger, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IBlurayExaminer blurayExaminer, ILocalizationManager localization, IApplicationPaths appPaths, IJsonSerializer json, IEncodingManager encodingManager, IFileSystem fileSystem, IServerConfigurationManager config, ISubtitleManager subtitleManager) { _logger = logger; _isoManager = isoManager; @@ -50,6 +56,8 @@ namespace MediaBrowser.Providers.MediaInfo _json = json; _encodingManager = encodingManager; _fileSystem = fileSystem; + _config = config; + _subtitleManager = subtitleManager; } public async Task ProbeVideo(T item, IDirectoryService directoryService, CancellationToken cancellationToken) @@ -118,7 +126,7 @@ namespace MediaBrowser.Providers.MediaInfo cancellationToken.ThrowIfCancellationRequested(); var idString = item.Id.ToString("N"); - var cachePath = Path.Combine(_appPaths.CachePath, + var cachePath = Path.Combine(_appPaths.CachePath, "ffprobe-video", idString.Substring(0, 2), idString, "v" + SchemaVersion + _mediaEncoder.Version + item.DateModified.Ticks.ToString(_usCulture) + ".json"); @@ -200,7 +208,7 @@ namespace MediaBrowser.Providers.MediaInfo FetchBdInfo(video, chapters, mediaStreams, blurayInfo); } - AddExternalSubtitles(video, mediaStreams, directoryService); + await AddExternalSubtitles(video, mediaStreams, directoryService, cancellationToken).ConfigureAwait(false); FetchWtvInfo(video, data); @@ -247,7 +255,7 @@ namespace MediaBrowser.Providers.MediaInfo } } - info.StartPositionTicks = chapter.start/100; + info.StartPositionTicks = chapter.start / 100; return info; } @@ -450,11 +458,42 @@ namespace MediaBrowser.Providers.MediaInfo /// /// The video. /// The current streams. - private void AddExternalSubtitles(Video video, List currentStreams, IDirectoryService directoryService) + private async Task AddExternalSubtitles(Video video, List currentStreams, IDirectoryService directoryService, CancellationToken cancellationToken) + { + var externalSubtitleStreams = GetExternalSubtitleStreams(video, currentStreams.Count, directoryService).ToList(); + + if ((_config.Configuration.SubtitleOptions.DownloadEpisodeSubtitles && + video is Episode) || + (_config.Configuration.SubtitleOptions.DownloadMovieSubtitles && + video is Movie)) + { + var downloadedLanguages = await new SubtitleDownloader(_logger, + _subtitleManager) + .DownloadSubtitles(video, + currentStreams, + externalSubtitleStreams, + _config.Configuration.SubtitleOptions.RequireExternalSubtitles, + _config.Configuration.SubtitleOptions.SubtitleDownloadLanguages, + cancellationToken).ConfigureAwait(false); + + // Rescan + if (downloadedLanguages.Count > 0) + { + externalSubtitleStreams = GetExternalSubtitleStreams(video, currentStreams.Count, directoryService).ToList(); + } + } + + video.SubtitleFiles = externalSubtitleStreams.Select(i => i.Path).OrderBy(i => i).ToList(); + + currentStreams.AddRange(externalSubtitleStreams); + } + + private IEnumerable GetExternalSubtitleStreams(Video video, + int startIndex, + IDirectoryService directoryService) { var files = GetSubtitleFiles(video, directoryService); - var startIndex = currentStreams.Count; var streams = new List(); var videoFileNameWithoutExtension = Path.GetFileNameWithoutExtension(video.Path); @@ -504,9 +543,7 @@ namespace MediaBrowser.Providers.MediaInfo } } - video.SubtitleFiles = streams.Select(i => i.Path).OrderBy(i => i).ToList(); - - currentStreams.AddRange(streams); + return streams; } /// @@ -627,7 +664,7 @@ namespace MediaBrowser.Providers.MediaInfo { var path = mount == null ? item.Path : mount.MountedPath; var dvd = new Dvd(path); - + var primaryTitle = dvd.Titles.OrderByDescending(GetRuntime).FirstOrDefault(); byte? titleNumber = null; diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs b/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs new file mode 100644 index 0000000000..7f7ccda193 --- /dev/null +++ b/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs @@ -0,0 +1,140 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Subtitles; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Providers.MediaInfo +{ + public class SubtitleDownloader + { + private readonly ILogger _logger; + private readonly ISubtitleManager _subtitleManager; + + public SubtitleDownloader(ILogger logger, ISubtitleManager subtitleManager) + { + _logger = logger; + _subtitleManager = subtitleManager; + } + + public async Task> DownloadSubtitles(Video video, + List internalSubtitleStreams, + List externalSubtitleStreams, + bool forceExternal, + IEnumerable languages, + CancellationToken cancellationToken) + { + if (video.LocationType != LocationType.FileSystem || + video.VideoType != VideoType.VideoFile) + { + return new List(); + } + + SubtitleMediaType mediaType; + + if (video is Episode) + { + mediaType = SubtitleMediaType.Episode; + } + else if (video is Movie) + { + mediaType = SubtitleMediaType.Movie; + } + else + { + // These are the only supported types + return new List(); + } + + var downloadedLanguages = new List(); + + foreach (var lang in languages) + { + try + { + var downloaded = await DownloadSubtitles(video, internalSubtitleStreams, externalSubtitleStreams, forceExternal, lang, mediaType, cancellationToken) + .ConfigureAwait(false); + + if (downloaded) + { + downloadedLanguages.Add(lang); + } + } + catch (Exception ex) + { + _logger.ErrorException("Error downloading subtitles", ex); + } + } + + return downloadedLanguages; + } + + private async Task DownloadSubtitles(Video video, + IEnumerable internalSubtitleStreams, + IEnumerable externalSubtitleStreams, + bool forceExternal, + string language, + SubtitleMediaType mediaType, + CancellationToken cancellationToken) + { + // There's already subtitles for this language + if (externalSubtitleStreams.Any(i => string.Equals(i.Language, language, StringComparison.OrdinalIgnoreCase))) + { + return false; + } + + // There's an internal subtitle stream for this language + if (!forceExternal && internalSubtitleStreams.Any(i => string.Equals(i.Language, language, StringComparison.OrdinalIgnoreCase))) + { + return false; + } + + var request = new SubtitleSearchRequest + { + ContentType = mediaType, + IndexNumber = video.IndexNumber, + Language = language, + MediaPath = video.Path, + Name = video.Name, + ParentIndexNumber = video.ParentIndexNumber, + ProductionYear = video.ProductionYear, + ProviderIds = video.ProviderIds + }; + + var episode = video as Episode; + + if (episode != null) + { + request.IndexNumberEnd = episode.IndexNumberEnd; + request.SeriesName = episode.SeriesName; + } + + try + { + var searchResults = await _subtitleManager.SearchSubtitles(request, cancellationToken).ConfigureAwait(false); + + var result = searchResults.FirstOrDefault(); + + if (result != null) + { + await _subtitleManager.DownloadSubtitles(video, result.Id, result.ProviderName, cancellationToken) + .ConfigureAwait(false); + + return true; + } + } + catch (Exception ex) + { + _logger.ErrorException("Error downloading subtitles", ex); + } + + return false; + } + } +} diff --git a/MediaBrowser.Providers/Subtitles/OpenSubtitleDownloader.cs b/MediaBrowser.Providers/Subtitles/OpenSubtitleDownloader.cs index 7309513d63..929cccd5ab 100644 --- a/MediaBrowser.Providers/Subtitles/OpenSubtitleDownloader.cs +++ b/MediaBrowser.Providers/Subtitles/OpenSubtitleDownloader.cs @@ -1,8 +1,10 @@ -using MediaBrowser.Common.Net; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Providers; +using MediaBrowser.Controller.Subtitles; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; -using MediaBrowser.Model.MediaInfo; +using MediaBrowser.Model.Providers; using OpenSubtitlesHandler; using System; using System.Collections.Generic; @@ -20,9 +22,9 @@ namespace MediaBrowser.Providers.Subtitles private readonly IHttpClient _httpClient; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - public OpenSubtitleDownloader(ILogger logger, IHttpClient httpClient) + public OpenSubtitleDownloader(ILogManager logManager, IHttpClient httpClient) { - _logger = logger; + _logger = logManager.GetLogger(GetType().Name); _httpClient = httpClient; } @@ -36,39 +38,71 @@ namespace MediaBrowser.Providers.Subtitles get { return new[] { SubtitleMediaType.Episode, SubtitleMediaType.Movie }; } } - public Task GetSubtitles(SubtitleRequest request, CancellationToken cancellationToken) + public Task GetSubtitles(string id, CancellationToken cancellationToken) { - return GetSubtitlesInternal(request, cancellationToken); + return GetSubtitlesInternal(id, cancellationToken); } - private async Task GetSubtitlesInternal(SubtitleRequest request, + private async Task GetSubtitlesInternal(string id, CancellationToken cancellationToken) { - var response = new SubtitleResponse(); + if (string.IsNullOrWhiteSpace(id)) + { + throw new ArgumentNullException("id"); + } + + var idParts = id.Split(new[] { '-' }, 3); + + var format = idParts[0]; + var language = idParts[1]; + var ossId = idParts[2]; + + var downloadsList = new[] { int.Parse(ossId, _usCulture) }; + + var resultDownLoad = OpenSubtitles.DownloadSubtitles(downloadsList); + if (!(resultDownLoad is MethodResponseSubtitleDownload)) + { + throw new ApplicationException("Invalid response type"); + } + var res = ((MethodResponseSubtitleDownload)resultDownLoad).Results.First(); + var data = Convert.FromBase64String(res.Data); + + return new SubtitleResponse + { + Format = format, + Language = language, + + Stream = new MemoryStream(Utilities.Decompress(new MemoryStream(data))) + }; + } + + public async Task> SearchSubtitles(SubtitleSearchRequest request, CancellationToken cancellationToken) + { var imdbIdText = request.GetProviderId(MetadataProviders.Imdb); long imdbId; if (string.IsNullOrWhiteSpace(imdbIdText) || - long.TryParse(imdbIdText.TrimStart('t'), NumberStyles.Any, _usCulture, out imdbId)) + !long.TryParse(imdbIdText.TrimStart('t'), NumberStyles.Any, _usCulture, out imdbId)) { - return response; + _logger.Debug("Imdb id missing"); + return new List(); } - + switch (request.ContentType) { case SubtitleMediaType.Episode: if (!request.IndexNumber.HasValue || !request.ParentIndexNumber.HasValue || string.IsNullOrEmpty(request.SeriesName)) { - _logger.Debug("Information Missing"); - return response; + _logger.Debug("Episode information missing"); + return new List(); } break; case SubtitleMediaType.Movie: if (string.IsNullOrEmpty(request.Name)) { - _logger.Debug("Information Missing"); - return response; + _logger.Debug("Movie name missing"); + return new List(); } break; } @@ -76,16 +110,18 @@ namespace MediaBrowser.Providers.Subtitles if (string.IsNullOrEmpty(request.MediaPath)) { _logger.Debug("Path Missing"); - return response; + return new List(); } Utilities.HttpClient = _httpClient; OpenSubtitles.SetUserAgent("OS Test User Agent"); - var loginResponse = OpenSubtitles.LogIn("", "", "en"); + + var loginResponse = await OpenSubtitles.LogInAsync("", "", "en", cancellationToken).ConfigureAwait(false); + if (!(loginResponse is MethodResponseLogIn)) { _logger.Debug("Login error"); - return response; + return new List(); } var subLanguageId = request.Language; @@ -105,54 +141,42 @@ namespace MediaBrowser.Providers.Subtitles var result = OpenSubtitles.SearchSubtitles(parms.ToArray()); if (!(result is MethodResponseSubtitleSearch)) { - _logger.Debug("invalid response type"); - return null; + _logger.Debug("Invalid response type"); + return new List(); } Predicate mediaFilter = x => request.ContentType == SubtitleMediaType.Episode - ? int.Parse(x.SeriesSeason) == request.ParentIndexNumber && int.Parse(x.SeriesEpisode) == request.IndexNumber - : long.Parse(x.IDMovieImdb) == imdbId; + ? int.Parse(x.SeriesSeason, _usCulture) == request.ParentIndexNumber && int.Parse(x.SeriesEpisode, _usCulture) == request.IndexNumber + : long.Parse(x.IDMovieImdb, _usCulture) == imdbId; var results = ((MethodResponseSubtitleSearch)result).Results; - var bestResult = results.Where(x => x.SubBad == "0" && mediaFilter(x)) - .OrderBy(x => x.MovieHash == hash) - .ThenBy(x => Math.Abs(long.Parse(x.MovieByteSize) - movieByteSize)) - .ThenByDescending(x => int.Parse(x.SubDownloadsCnt)) - .ThenByDescending(x => double.Parse(x.SubRating)) - .ToList(); - - if (!bestResult.Any()) - { - _logger.Debug("No Subtitles"); - return response; - } - - _logger.Debug("Found " + bestResult.Count + " subtitles."); - var subtitle = bestResult.First(); - var downloadsList = new[] { int.Parse(subtitle.IDSubtitleFile) }; + // Avoid implicitly captured closure + var hasCopy = hash; - var resultDownLoad = OpenSubtitles.DownloadSubtitles(downloadsList); - if (!(resultDownLoad is MethodResponseSubtitleDownload)) - { - _logger.Debug("invalid response type"); - return response; - } - if (!((MethodResponseSubtitleDownload)resultDownLoad).Results.Any()) - { - _logger.Debug("No Subtitle Downloads"); - return response; - } - - var res = ((MethodResponseSubtitleDownload)resultDownLoad).Results.First(); - var data = Convert.FromBase64String(res.Data); - - response.HasContent = true; - response.Format = subtitle.SubFormat.ToUpper(); - response.Stream = new MemoryStream(Utilities.Decompress(new MemoryStream(data))); - return response; + return results.Where(x => x.SubBad == "0" && mediaFilter(x)) + .OrderBy(x => x.MovieHash == hash) + .ThenBy(x => Math.Abs(long.Parse(x.MovieByteSize, _usCulture) - movieByteSize)) + .ThenByDescending(x => int.Parse(x.SubDownloadsCnt, _usCulture)) + .ThenByDescending(x => double.Parse(x.SubRating, _usCulture)) + .Select(i => new RemoteSubtitleInfo + { + Author = i.UserNickName, + Comment = i.SubAuthorComment, + CommunityRating = float.Parse(i.SubRating, _usCulture), + DownloadCount = int.Parse(i.SubDownloadsCnt, _usCulture), + Format = i.SubFormat, + ProviderName = Name, + Language = i.SubLanguageID, + + Id = i.SubFormat + "-" + i.SubLanguageID + "-" + i.IDSubtitle, + + Name = i.SubFileName, + DateCreated = DateTime.Parse(i.SubAddDate, _usCulture), + IsHashMatch = i.MovieHash == hasCopy + }); } } } diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs new file mode 100644 index 0000000000..6951e8bd08 --- /dev/null +++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs @@ -0,0 +1,141 @@ +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Subtitles; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Providers; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Providers.Subtitles +{ + public class SubtitleManager : ISubtitleManager + { + private ISubtitleProvider[] _subtitleProviders; + private readonly ILogger _logger; + private readonly IFileSystem _fileSystem; + private readonly ILibraryMonitor _monitor; + + public SubtitleManager(ILogger logger, IFileSystem fileSystem, ILibraryMonitor monitor) + { + _logger = logger; + _fileSystem = fileSystem; + _monitor = monitor; + } + + public void AddParts(IEnumerable subtitleProviders) + { + _subtitleProviders = subtitleProviders.ToArray(); + } + + public async Task> SearchSubtitles(SubtitleSearchRequest request, CancellationToken cancellationToken) + { + var providers = _subtitleProviders + .Where(i => i.SupportedMediaTypes.Contains(request.ContentType)) + .ToList(); + + var tasks = providers.Select(async i => + { + try + { + return await i.SearchSubtitles(request, cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error downloading subtitles from {0}", ex, i.Name); + return new List(); + } + }); + + var results = await Task.WhenAll(tasks).ConfigureAwait(false); + + return results.SelectMany(i => i); + } + + public async Task DownloadSubtitles(Video video, + string subtitleId, + string providerName, + CancellationToken cancellationToken) + { + var provider = _subtitleProviders.First(i => string.Equals(i.Name, providerName, StringComparison.OrdinalIgnoreCase)); + + var response = await provider.GetSubtitles(subtitleId, cancellationToken).ConfigureAwait(false); + + using (var stream = response.Stream) + { + var savePath = Path.Combine(Path.GetDirectoryName(video.Path), + Path.GetFileNameWithoutExtension(video.Path) + "." + response.Language.ToLower() + "." + response.Format.ToLower()); + + _logger.Info("Saving subtitles to {0}", savePath); + + _monitor.ReportFileSystemChangeBeginning(savePath); + + try + { + using (var fs = _fileSystem.GetFileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.Read, true)) + { + await stream.CopyToAsync(fs).ConfigureAwait(false); + } + } + finally + { + _monitor.ReportFileSystemChangeComplete(savePath, false); + } + } + } + + public Task> SearchSubtitles(Video video, string language, CancellationToken cancellationToken) + { + if (video.LocationType != LocationType.FileSystem || + video.VideoType != VideoType.VideoFile) + { + return Task.FromResult>(new List()); + } + + SubtitleMediaType mediaType; + + if (video is Episode) + { + mediaType = SubtitleMediaType.Episode; + } + else if (video is Movie) + { + mediaType = SubtitleMediaType.Movie; + } + else + { + // These are the only supported types + return Task.FromResult>(new List()); + } + + var request = new SubtitleSearchRequest + { + ContentType = mediaType, + IndexNumber = video.IndexNumber, + Language = language, + MediaPath = video.Path, + Name = video.Name, + ParentIndexNumber = video.ParentIndexNumber, + ProductionYear = video.ProductionYear, + ProviderIds = video.ProviderIds + }; + + var episode = video as Episode; + + if (episode != null) + { + request.IndexNumberEnd = episode.IndexNumberEnd; + request.SeriesName = episode.SeriesName; + } + + return SearchSubtitles(request, cancellationToken); + } + } +} diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs index f516c08780..748bc4b9c3 100644 --- a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs +++ b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs @@ -327,7 +327,7 @@ namespace MediaBrowser.Server.Implementations.Channels var categoryKey = string.IsNullOrWhiteSpace(categoryId) ? "root" : categoryId.GetMD5().ToString("N"); - return Path.Combine(_config.ApplicationPaths.CachePath, channelId, categoryKey, user.Id.ToString("N") + ".json"); + return Path.Combine(_config.ApplicationPaths.CachePath, "channels", channelId, categoryKey, user.Id.ToString("N") + ".json"); } private async Task> GetReturnItems(IEnumerable items, User user, ChannelItemQuery query, CancellationToken cancellationToken) diff --git a/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs b/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs index 653cbacb61..adcf3edba5 100644 --- a/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs +++ b/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs @@ -93,7 +93,13 @@ namespace MediaBrowser.Server.Implementations.Collections // Find an actual physical folder if (folder is CollectionFolder) { - return _libraryManager.RootFolder.Children.OfType().First(i => folder.PhysicalLocations.Contains(i.Path, StringComparer.OrdinalIgnoreCase)); + var child = _libraryManager.RootFolder.Children.OfType() + .FirstOrDefault(i => folder.PhysicalLocations.Contains(i.Path, StringComparer.OrdinalIgnoreCase)); + + if (child != null) + { + return child; + } } } diff --git a/MediaBrowser.Server.Implementations/HttpServer/ServerLogger.cs b/MediaBrowser.Server.Implementations/HttpServer/ServerLogger.cs index 011e64df2f..7a4f922eda 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/ServerLogger.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/ServerLogger.cs @@ -206,7 +206,8 @@ namespace MediaBrowser.Server.Implementations.HttpServer /// The message. public void Warn(object message) { - _logger.Warn(GetMesssage(message)); + // Hide StringMapTypeDeserializer messages + // _logger.Warn(GetMesssage(message)); } /// @@ -216,7 +217,8 @@ namespace MediaBrowser.Server.Implementations.HttpServer /// The args. public void WarnFormat(string format, params object[] args) { - _logger.Warn(format, args); + // Hide StringMapTypeDeserializer messages + // _logger.Warn(format, args); } /// diff --git a/MediaBrowser.Server.Implementations/Library/Validators/PeoplePostScanTask.cs b/MediaBrowser.Server.Implementations/Library/Validators/PeoplePostScanTask.cs deleted file mode 100644 index d11e62a1a2..0000000000 --- a/MediaBrowser.Server.Implementations/Library/Validators/PeoplePostScanTask.cs +++ /dev/null @@ -1,44 +0,0 @@ -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Logging; -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Server.Implementations.Library.Validators -{ - class PeoplePostScanTask : ILibraryPostScanTask - { - /// - /// The _library manager - /// - private readonly ILibraryManager _libraryManager; - - /// - /// The _logger - /// - private readonly ILogger _logger; - - public PeoplePostScanTask(ILibraryManager libraryManager, ILogger logger) - { - _libraryManager = libraryManager; - _logger = logger; - } - - /// - /// Runs the specified progress. - /// - /// The progress. - /// The cancellation token. - /// Task. - public Task Run(IProgress progress, CancellationToken cancellationToken) - { - return new PeopleValidator(_libraryManager, _logger).ValidatePeople(cancellationToken, new MetadataRefreshOptions - { - ImageRefreshMode = ImageRefreshMode.ValidationOnly, - MetadataRefreshMode = MetadataRefreshMode.None - - }, progress); - } - } -} diff --git a/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs b/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs index 629d21df64..8eaaaacc05 100644 --- a/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs +++ b/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs @@ -5,7 +5,6 @@ using MediaBrowser.Controller.Localization; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Serialization; -using MoreLinq; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -106,16 +105,13 @@ namespace MediaBrowser.Server.Implementations.Localization /// IEnumerable{CultureDto}. public IEnumerable GetCultures() { - return CultureInfo.GetCultures(CultureTypes.AllCultures) - .OrderBy(c => c.DisplayName) - .DistinctBy(c => c.TwoLetterISOLanguageName + c.ThreeLetterISOLanguageName) - .Select(c => new CultureDto - { - Name = c.Name, - DisplayName = c.DisplayName, - ThreeLetterISOLanguageName = c.ThreeLetterISOLanguageName, - TwoLetterISOLanguageName = c.TwoLetterISOLanguageName - }); + var type = GetType(); + var path = type.Namespace + ".cultures.json"; + + using (var stream = type.Assembly.GetManifestResourceStream(path)) + { + return _jsonSerializer.DeserializeFromStream>(stream); + } } /// @@ -124,28 +120,13 @@ namespace MediaBrowser.Server.Implementations.Localization /// IEnumerable{CountryInfo}. public IEnumerable GetCountries() { - return CultureInfo.GetCultures(CultureTypes.SpecificCultures) - .Select(c => - { - try - { - return new RegionInfo(c.LCID); - } - catch (CultureNotFoundException) - { - return null; - } - }) - .Where(i => i != null) - .OrderBy(c => c.DisplayName) - .DistinctBy(c => c.TwoLetterISORegionName) - .Select(c => new CountryInfo - { - Name = c.Name, - DisplayName = c.DisplayName, - TwoLetterISORegionName = c.TwoLetterISORegionName, - ThreeLetterISORegionName = c.ThreeLetterISORegionName - }); + var type = GetType(); + var path = type.Namespace + ".countries.json"; + + using (var stream = type.Assembly.GetManifestResourceStream(path)) + { + return _jsonSerializer.DeserializeFromStream>(stream); + } } /// diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index 0c99b3a573..c2e29649fd 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -627,5 +627,84 @@ "OptionSpecialFeatures": "Special Features", "HeaderCollections": "Collections", "HeaderChannels": "Channels", - "HeaderMyLibrary": "My Library" + "HeaderMyLibrary": "My Library", + "LabelProfileCodecsHelp": "Separated by comma. This can be left empty to apply to all codecs.", + "LabelProfileContainersHelp": "Separated by comma. This can be left empty to apply to all containers.", + "HeaderResponseProfile": "Response Profile", + "LabelType": "Type:", + "LabelProfileContainer": "Container:", + "LabelProfileVideoCodecs": "Video codecs:", + "LabelProfileAudioCodecs": "Audio codecs:", + "LabelProfileCodecs": "Codecs:", + "HeaderDirectPlayProfile": "Direct Play Profile", + "HeaderTranscodingProfile": "Transcoding Profile", + "HeaderCodecProfile": "Codec Profile", + "HeaderCodecProfileHelp": "Define additional conditions that must be met in order for a codec to be direct played.", + "HeaderContainerProfile": "Container Profile", + "HeaderContainerProfileHelp": "Define additional conditions that must be met in order for a file to be direct played.", + "OptionProfileVideo": "Video", + "OptionProfileAudio": "Audio", + "OptionProfileVideoAudio": "Video Audio", + "OptionProfilePhoto": "Photo", + "LabelUserLibrary": "User library:", + "LabelUserLibraryHelp": "Select which user library to display to the device. Leave empty to inherit the default setting.", + "OptionPlainStorageFolders": "Display all folders as plain storage folders", + "OptionPlainStorageFoldersHelp": "If enabled, all folders are represented in DIDL as \"object.container.storageFolder\" instead of a more specific type, such as \"object.container.person.musicArtist\".", + "OptionPlainVideoItems": "Display all videos as plain video items", + "OptionPlainVideoItemsHelp": "If enabled, all videos are represented in DIDL as \"object.item.videoItem\" instead of a more specific type, such as \"object.item.videoItem.movie\".", + "LabelSupportedMediaTypes": "Supported Media Types:", + "TabIdentification": "Identification", + "TabDirectPlay": "Direct Play", + "TabContainers": "Containers", + "TabCodecs": "Codecs", + "TabResponses": "Responses", + "HeaderProfileInformation": "Profile Information", + "LabelEmbedAlbumArtDidl": "Embed album art in Didl", + "LabelEmbedAlbumArtDidlHelp": "Some devices prefer this method for obtaining album art. Others may fail to play with this option enabled.", + "LabelAlbumArtPN": "Album art PN:", + "LabelAlbumArtHelp": "PN used for album art, within the dlna:profileID attribute on upnp:albumArtURI. Some clients require a specific value, regardless of the size of the image.", + "LabelAlbumArtMaxWidth": "Album art max width:", + "LabelAlbumArtMaxWidthHelp": "Max resolution of album art exposed via upnp:albumArtURI.", + "LabelAlbumArtMaxHeight": "Album art max height:", + "LabelAlbumArtMaxHeightHelp": "Max resolution of album art exposed via upnp:albumArtURI.", + "LabelIconMaxWidth": "Icon max width:", + "LabelIconMaxWidthHelp": "Max resolution of icons exposed via upnp:icon.", + "LabelIconMaxHeight": "Icon max height:", + "LabelIconMaxHeightHelp": "Max resolution of icons exposed via upnp:icon.", + "LabelIdentificationFieldHelp": "A case-insensitive substring or regex expression.", + "HeaderProfileServerSettingsHelp": "These values control how Media Browser will present itself to the device.", + "LabelMaxBitrate": "Max bitrate:", + "LabelMaxBitrateHelp": "Specify a max bitrate in bandwidth constrained environments, or if the device imposes it's own limit.", + "OptionIgnoreTranscodeByteRangeRequests": "Ignore transcode byte range requests", + "OptionIgnoreTranscodeByteRangeRequestsHelp": "If enabled, these requests will be honored but will ignore the byte range header.", + "LabelFriendlyName": "Friendly name", + "LabelManufacturer": "Manufacturer", + "LabelManufacturerUrl": "Manufacturer url", + "LabelModelName": "Model name", + "LabelModelNumber": "Model number", + "LabelModelDescription": "Model description", + "LabelModelUrl": "Model url", + "LabelSerialNumber": "Serial number", + "LabelDeviceDescription": "Device description", + "HeaderIdentificationCriteriaHelp": "Enter at least one identification criteria.", + "HeaderDirectPlayProfileHelp": "Add direct play profiles to indicate which formats the device can handle natively.", + "HeaderTranscodingProfileHelp": "Add transcoding profiles to indicate which formats should be used when transcoding is required.", + "HeaderContainerProfileHelp": "Container profiles indicate the limitations of a device when playing specific formats. If a limitation applies then the media will be transcoded, even if the format is configured for direct play.", + "HeaderCodecProfileHelp": "Codec profiles indicate the limitations of a device when playing specific codecs. If a limitation applies then the media will be transcoded, even if the codec is configured for direct play.", + "HeaderResponseProfileHelp": "Response profiles provide a way to customize information sent to the device when playing certain kinds of media.", + "LabelXDlnaCap": "X-Dlna cap:", + "LabelXDlnaCapHelp": "Determines the content of the X_DLNACAP element in the urn:schemas-dlna-org:device-1-0 namespace.", + "LabelXDlnaDoc": "X-Dlna doc:", + "LabelXDlnaDocHelp": "Determines the content of the X_DLNADOC element in the urn:schemas-dlna-org:device-1-0 namespace.", + "LabelSonyAggregationFlags": "Sony aggregation flags:", + "LabelSonyAggregationFlagsHelp": "Determines the content of the aggregationFlags element in the urn:schemas-sonycom:av namespace.", + "LabelTranscodingContainer": "Container:", + "LabelTranscodingVideoCodec": "Video codec:", + "LabelTranscodingVideoProfile": "Video profile:", + "LabelTranscodingAudioCodec": "Audio codec:", + "OptionEnableM2tsMode": "Enable M2ts mode", + "OptionEnableM2tsModeHelp": "Enable m2ts mode when encoding to mpegts.", + "OptionEstimateContentLength": "Estimate content length when transcoding", + "OptionReportByteRangeSeekingWhenTranscoding": "Report that the server supports byte seeking when transcoding", + "OptionReportByteRangeSeekingWhenTranscodingHelp": "This is required for some devices that don't time seek very well." } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/countries.json b/MediaBrowser.Server.Implementations/Localization/countries.json new file mode 100644 index 0000000000..e671b36853 --- /dev/null +++ b/MediaBrowser.Server.Implementations/Localization/countries.json @@ -0,0 +1 @@ +[{"Name":"AF","DisplayName":"Afghanistan","TwoLetterISORegionName":"AF","ThreeLetterISORegionName":"AFG"},{"Name":"AL","DisplayName":"Albania","TwoLetterISORegionName":"AL","ThreeLetterISORegionName":"ALB"},{"Name":"DZ","DisplayName":"Algeria","TwoLetterISORegionName":"DZ","ThreeLetterISORegionName":"DZA"},{"Name":"AR","DisplayName":"Argentina","TwoLetterISORegionName":"AR","ThreeLetterISORegionName":"ARG"},{"Name":"AM","DisplayName":"Armenia","TwoLetterISORegionName":"AM","ThreeLetterISORegionName":"ARM"},{"Name":"AU","DisplayName":"Australia","TwoLetterISORegionName":"AU","ThreeLetterISORegionName":"AUS"},{"Name":"AT","DisplayName":"Austria","TwoLetterISORegionName":"AT","ThreeLetterISORegionName":"AUT"},{"Name":"AZ","DisplayName":"Azerbaijan","TwoLetterISORegionName":"AZ","ThreeLetterISORegionName":"AZE"},{"Name":"BH","DisplayName":"Bahrain","TwoLetterISORegionName":"BH","ThreeLetterISORegionName":"BHR"},{"Name":"BD","DisplayName":"Bangladesh","TwoLetterISORegionName":"BD","ThreeLetterISORegionName":"BGD"},{"Name":"BY","DisplayName":"Belarus","TwoLetterISORegionName":"BY","ThreeLetterISORegionName":"BLR"},{"Name":"BE","DisplayName":"Belgium","TwoLetterISORegionName":"BE","ThreeLetterISORegionName":"BEL"},{"Name":"BZ","DisplayName":"Belize","TwoLetterISORegionName":"BZ","ThreeLetterISORegionName":"BLZ"},{"Name":"VE","DisplayName":"Bolivarian Republic of Venezuela","TwoLetterISORegionName":"VE","ThreeLetterISORegionName":"VEN"},{"Name":"BO","DisplayName":"Bolivia","TwoLetterISORegionName":"BO","ThreeLetterISORegionName":"BOL"},{"Name":"BA","DisplayName":"Bosnia and Herzegovina","TwoLetterISORegionName":"BA","ThreeLetterISORegionName":"BIH"},{"Name":"BW","DisplayName":"Botswana","TwoLetterISORegionName":"BW","ThreeLetterISORegionName":"BWA"},{"Name":"BR","DisplayName":"Brazil","TwoLetterISORegionName":"BR","ThreeLetterISORegionName":"BRA"},{"Name":"BN","DisplayName":"Brunei Darussalam","TwoLetterISORegionName":"BN","ThreeLetterISORegionName":"BRN"},{"Name":"BG","DisplayName":"Bulgaria","TwoLetterISORegionName":"BG","ThreeLetterISORegionName":"BGR"},{"Name":"KH","DisplayName":"Cambodia","TwoLetterISORegionName":"KH","ThreeLetterISORegionName":"KHM"},{"Name":"CM","DisplayName":"Cameroon","TwoLetterISORegionName":"CM","ThreeLetterISORegionName":"CMR"},{"Name":"CA","DisplayName":"Canada","TwoLetterISORegionName":"CA","ThreeLetterISORegionName":"CAN"},{"Name":"029","DisplayName":"Caribbean","TwoLetterISORegionName":"029","ThreeLetterISORegionName":"029"},{"Name":"CL","DisplayName":"Chile","TwoLetterISORegionName":"CL","ThreeLetterISORegionName":"CHL"},{"Name":"CO","DisplayName":"Colombia","TwoLetterISORegionName":"CO","ThreeLetterISORegionName":"COL"},{"Name":"CD","DisplayName":"Congo [DRC]","TwoLetterISORegionName":"CD","ThreeLetterISORegionName":"COD"},{"Name":"CR","DisplayName":"Costa Rica","TwoLetterISORegionName":"CR","ThreeLetterISORegionName":"CRI"},{"Name":"HR","DisplayName":"Croatia","TwoLetterISORegionName":"HR","ThreeLetterISORegionName":"HRV"},{"Name":"CZ","DisplayName":"Czech Republic","TwoLetterISORegionName":"CZ","ThreeLetterISORegionName":"CZE"},{"Name":"DK","DisplayName":"Denmark","TwoLetterISORegionName":"DK","ThreeLetterISORegionName":"DNK"},{"Name":"DO","DisplayName":"Dominican Republic","TwoLetterISORegionName":"DO","ThreeLetterISORegionName":"DOM"},{"Name":"EC","DisplayName":"Ecuador","TwoLetterISORegionName":"EC","ThreeLetterISORegionName":"ECU"},{"Name":"EG","DisplayName":"Egypt","TwoLetterISORegionName":"EG","ThreeLetterISORegionName":"EGY"},{"Name":"SV","DisplayName":"El Salvador","TwoLetterISORegionName":"SV","ThreeLetterISORegionName":"SLV"},{"Name":"ER","DisplayName":"Eritrea","TwoLetterISORegionName":"ER","ThreeLetterISORegionName":"ERI"},{"Name":"EE","DisplayName":"Estonia","TwoLetterISORegionName":"EE","ThreeLetterISORegionName":"EST"},{"Name":"ET","DisplayName":"Ethiopia","TwoLetterISORegionName":"ET","ThreeLetterISORegionName":"ETH"},{"Name":"FO","DisplayName":"Faroe Islands","TwoLetterISORegionName":"FO","ThreeLetterISORegionName":"FRO"},{"Name":"FI","DisplayName":"Finland","TwoLetterISORegionName":"FI","ThreeLetterISORegionName":"FIN"},{"Name":"FR","DisplayName":"France","TwoLetterISORegionName":"FR","ThreeLetterISORegionName":"FRA"},{"Name":"GE","DisplayName":"Georgia","TwoLetterISORegionName":"GE","ThreeLetterISORegionName":"GEO"},{"Name":"DE","DisplayName":"Germany","TwoLetterISORegionName":"DE","ThreeLetterISORegionName":"DEU"},{"Name":"GR","DisplayName":"Greece","TwoLetterISORegionName":"GR","ThreeLetterISORegionName":"GRC"},{"Name":"GL","DisplayName":"Greenland","TwoLetterISORegionName":"GL","ThreeLetterISORegionName":"GRL"},{"Name":"GT","DisplayName":"Guatemala","TwoLetterISORegionName":"GT","ThreeLetterISORegionName":"GTM"},{"Name":"HT","DisplayName":"Haiti","TwoLetterISORegionName":"HT","ThreeLetterISORegionName":"HTI"},{"Name":"HN","DisplayName":"Honduras","TwoLetterISORegionName":"HN","ThreeLetterISORegionName":"HND"},{"Name":"HK","DisplayName":"Hong Kong S.A.R.","TwoLetterISORegionName":"HK","ThreeLetterISORegionName":"HKG"},{"Name":"HU","DisplayName":"Hungary","TwoLetterISORegionName":"HU","ThreeLetterISORegionName":"HUN"},{"Name":"IS","DisplayName":"Iceland","TwoLetterISORegionName":"IS","ThreeLetterISORegionName":"ISL"},{"Name":"IN","DisplayName":"India","TwoLetterISORegionName":"IN","ThreeLetterISORegionName":"IND"},{"Name":"ID","DisplayName":"Indonesia","TwoLetterISORegionName":"ID","ThreeLetterISORegionName":"IDN"},{"Name":"IR","DisplayName":"Iran","TwoLetterISORegionName":"IR","ThreeLetterISORegionName":"IRN"},{"Name":"IQ","DisplayName":"Iraq","TwoLetterISORegionName":"IQ","ThreeLetterISORegionName":"IRQ"},{"Name":"IE","DisplayName":"Ireland","TwoLetterISORegionName":"IE","ThreeLetterISORegionName":"IRL"},{"Name":"PK","DisplayName":"Islamic Republic of Pakistan","TwoLetterISORegionName":"PK","ThreeLetterISORegionName":"PAK"},{"Name":"IL","DisplayName":"Israel","TwoLetterISORegionName":"IL","ThreeLetterISORegionName":"ISR"},{"Name":"IT","DisplayName":"Italy","TwoLetterISORegionName":"IT","ThreeLetterISORegionName":"ITA"},{"Name":"CI","DisplayName":"Ivory Coast","TwoLetterISORegionName":"CI","ThreeLetterISORegionName":"CIV"},{"Name":"JM","DisplayName":"Jamaica","TwoLetterISORegionName":"JM","ThreeLetterISORegionName":"JAM"},{"Name":"JP","DisplayName":"Japan","TwoLetterISORegionName":"JP","ThreeLetterISORegionName":"JPN"},{"Name":"JO","DisplayName":"Jordan","TwoLetterISORegionName":"JO","ThreeLetterISORegionName":"JOR"},{"Name":"KZ","DisplayName":"Kazakhstan","TwoLetterISORegionName":"KZ","ThreeLetterISORegionName":"KAZ"},{"Name":"KE","DisplayName":"Kenya","TwoLetterISORegionName":"KE","ThreeLetterISORegionName":"KEN"},{"Name":"KR","DisplayName":"Korea","TwoLetterISORegionName":"KR","ThreeLetterISORegionName":"KOR"},{"Name":"KW","DisplayName":"Kuwait","TwoLetterISORegionName":"KW","ThreeLetterISORegionName":"KWT"},{"Name":"KG","DisplayName":"Kyrgyzstan","TwoLetterISORegionName":"KG","ThreeLetterISORegionName":"KGZ"},{"Name":"LA","DisplayName":"Lao P.D.R.","TwoLetterISORegionName":"LA","ThreeLetterISORegionName":"LAO"},{"Name":"419","DisplayName":"Latin America","TwoLetterISORegionName":"419","ThreeLetterISORegionName":"419"},{"Name":"LV","DisplayName":"Latvia","TwoLetterISORegionName":"LV","ThreeLetterISORegionName":"LVA"},{"Name":"LB","DisplayName":"Lebanon","TwoLetterISORegionName":"LB","ThreeLetterISORegionName":"LBN"},{"Name":"LY","DisplayName":"Libya","TwoLetterISORegionName":"LY","ThreeLetterISORegionName":"LBY"},{"Name":"LI","DisplayName":"Liechtenstein","TwoLetterISORegionName":"LI","ThreeLetterISORegionName":"LIE"},{"Name":"LT","DisplayName":"Lithuania","TwoLetterISORegionName":"LT","ThreeLetterISORegionName":"LTU"},{"Name":"LU","DisplayName":"Luxembourg","TwoLetterISORegionName":"LU","ThreeLetterISORegionName":"LUX"},{"Name":"MO","DisplayName":"Macao S.A.R.","TwoLetterISORegionName":"MO","ThreeLetterISORegionName":"MAC"},{"Name":"MK","DisplayName":"Macedonia (FYROM)","TwoLetterISORegionName":"MK","ThreeLetterISORegionName":"MKD"},{"Name":"MY","DisplayName":"Malaysia","TwoLetterISORegionName":"MY","ThreeLetterISORegionName":"MYS"},{"Name":"MV","DisplayName":"Maldives","TwoLetterISORegionName":"MV","ThreeLetterISORegionName":"MDV"},{"Name":"ML","DisplayName":"Mali","TwoLetterISORegionName":"ML","ThreeLetterISORegionName":"MLI"},{"Name":"MT","DisplayName":"Malta","TwoLetterISORegionName":"MT","ThreeLetterISORegionName":"MLT"},{"Name":"MX","DisplayName":"Mexico","TwoLetterISORegionName":"MX","ThreeLetterISORegionName":"MEX"},{"Name":"MN","DisplayName":"Mongolia","TwoLetterISORegionName":"MN","ThreeLetterISORegionName":"MNG"},{"Name":"ME","DisplayName":"Montenegro","TwoLetterISORegionName":"ME","ThreeLetterISORegionName":"MNE"},{"Name":"MA","DisplayName":"Morocco","TwoLetterISORegionName":"MA","ThreeLetterISORegionName":"MAR"},{"Name":"NP","DisplayName":"Nepal","TwoLetterISORegionName":"NP","ThreeLetterISORegionName":"NPL"},{"Name":"NL","DisplayName":"Netherlands","TwoLetterISORegionName":"NL","ThreeLetterISORegionName":"NLD"},{"Name":"NZ","DisplayName":"New Zealand","TwoLetterISORegionName":"NZ","ThreeLetterISORegionName":"NZL"},{"Name":"NI","DisplayName":"Nicaragua","TwoLetterISORegionName":"NI","ThreeLetterISORegionName":"NIC"},{"Name":"NG","DisplayName":"Nigeria","TwoLetterISORegionName":"NG","ThreeLetterISORegionName":"NGA"},{"Name":"NO","DisplayName":"Norway","TwoLetterISORegionName":"NO","ThreeLetterISORegionName":"NOR"},{"Name":"OM","DisplayName":"Oman","TwoLetterISORegionName":"OM","ThreeLetterISORegionName":"OMN"},{"Name":"PA","DisplayName":"Panama","TwoLetterISORegionName":"PA","ThreeLetterISORegionName":"PAN"},{"Name":"PY","DisplayName":"Paraguay","TwoLetterISORegionName":"PY","ThreeLetterISORegionName":"PRY"},{"Name":"CN","DisplayName":"People's Republic of China","TwoLetterISORegionName":"CN","ThreeLetterISORegionName":"CHN"},{"Name":"PE","DisplayName":"Peru","TwoLetterISORegionName":"PE","ThreeLetterISORegionName":"PER"},{"Name":"PH","DisplayName":"Philippines","TwoLetterISORegionName":"PH","ThreeLetterISORegionName":"PHL"},{"Name":"PL","DisplayName":"Poland","TwoLetterISORegionName":"PL","ThreeLetterISORegionName":"POL"},{"Name":"PT","DisplayName":"Portugal","TwoLetterISORegionName":"PT","ThreeLetterISORegionName":"PRT"},{"Name":"MC","DisplayName":"Principality of Monaco","TwoLetterISORegionName":"MC","ThreeLetterISORegionName":"MCO"},{"Name":"PR","DisplayName":"Puerto Rico","TwoLetterISORegionName":"PR","ThreeLetterISORegionName":"PRI"},{"Name":"QA","DisplayName":"Qatar","TwoLetterISORegionName":"QA","ThreeLetterISORegionName":"QAT"},{"Name":"MD","DisplayName":"Republica Moldova","TwoLetterISORegionName":"MD","ThreeLetterISORegionName":"MDA"},{"Name":"RE","DisplayName":"Réunion","TwoLetterISORegionName":"RE","ThreeLetterISORegionName":"REU"},{"Name":"RO","DisplayName":"Romania","TwoLetterISORegionName":"RO","ThreeLetterISORegionName":"ROU"},{"Name":"RU","DisplayName":"Russia","TwoLetterISORegionName":"RU","ThreeLetterISORegionName":"RUS"},{"Name":"RW","DisplayName":"Rwanda","TwoLetterISORegionName":"RW","ThreeLetterISORegionName":"RWA"},{"Name":"SA","DisplayName":"Saudi Arabia","TwoLetterISORegionName":"SA","ThreeLetterISORegionName":"SAU"},{"Name":"SN","DisplayName":"Senegal","TwoLetterISORegionName":"SN","ThreeLetterISORegionName":"SEN"},{"Name":"RS","DisplayName":"Serbia","TwoLetterISORegionName":"RS","ThreeLetterISORegionName":"SRB"},{"Name":"CS","DisplayName":"Serbia and Montenegro (Former)","TwoLetterISORegionName":"CS","ThreeLetterISORegionName":"SCG"},{"Name":"SG","DisplayName":"Singapore","TwoLetterISORegionName":"SG","ThreeLetterISORegionName":"SGP"},{"Name":"SK","DisplayName":"Slovakia","TwoLetterISORegionName":"SK","ThreeLetterISORegionName":"SVK"},{"Name":"SI","DisplayName":"Slovenia","TwoLetterISORegionName":"SI","ThreeLetterISORegionName":"SVN"},{"Name":"SO","DisplayName":"Soomaaliya","TwoLetterISORegionName":"SO","ThreeLetterISORegionName":"SOM"},{"Name":"ZA","DisplayName":"South Africa","TwoLetterISORegionName":"ZA","ThreeLetterISORegionName":"ZAF"},{"Name":"ES","DisplayName":"Spain","TwoLetterISORegionName":"ES","ThreeLetterISORegionName":"ESP"},{"Name":"LK","DisplayName":"Sri Lanka","TwoLetterISORegionName":"LK","ThreeLetterISORegionName":"LKA"},{"Name":"SE","DisplayName":"Sweden","TwoLetterISORegionName":"SE","ThreeLetterISORegionName":"SWE"},{"Name":"CH","DisplayName":"Switzerland","TwoLetterISORegionName":"CH","ThreeLetterISORegionName":"CHE"},{"Name":"SY","DisplayName":"Syria","TwoLetterISORegionName":"SY","ThreeLetterISORegionName":"SYR"},{"Name":"TW","DisplayName":"Taiwan","TwoLetterISORegionName":"TW","ThreeLetterISORegionName":"TWN"},{"Name":"TJ","DisplayName":"Tajikistan","TwoLetterISORegionName":"TJ","ThreeLetterISORegionName":"TAJ"},{"Name":"TH","DisplayName":"Thailand","TwoLetterISORegionName":"TH","ThreeLetterISORegionName":"THA"},{"Name":"TT","DisplayName":"Trinidad and Tobago","TwoLetterISORegionName":"TT","ThreeLetterISORegionName":"TTO"},{"Name":"TN","DisplayName":"Tunisia","TwoLetterISORegionName":"TN","ThreeLetterISORegionName":"TUN"},{"Name":"TR","DisplayName":"Turkey","TwoLetterISORegionName":"TR","ThreeLetterISORegionName":"TUR"},{"Name":"TM","DisplayName":"Turkmenistan","TwoLetterISORegionName":"TM","ThreeLetterISORegionName":"TKM"},{"Name":"AE","DisplayName":"U.A.E.","TwoLetterISORegionName":"AE","ThreeLetterISORegionName":"ARE"},{"Name":"UA","DisplayName":"Ukraine","TwoLetterISORegionName":"UA","ThreeLetterISORegionName":"UKR"},{"Name":"GB","DisplayName":"United Kingdom","TwoLetterISORegionName":"GB","ThreeLetterISORegionName":"GBR"},{"Name":"US","DisplayName":"United States","TwoLetterISORegionName":"US","ThreeLetterISORegionName":"USA"},{"Name":"UY","DisplayName":"Uruguay","TwoLetterISORegionName":"UY","ThreeLetterISORegionName":"URY"},{"Name":"UZ","DisplayName":"Uzbekistan","TwoLetterISORegionName":"UZ","ThreeLetterISORegionName":"UZB"},{"Name":"VN","DisplayName":"Vietnam","TwoLetterISORegionName":"VN","ThreeLetterISORegionName":"VNM"},{"Name":"YE","DisplayName":"Yemen","TwoLetterISORegionName":"YE","ThreeLetterISORegionName":"YEM"},{"Name":"ZW","DisplayName":"Zimbabwe","TwoLetterISORegionName":"ZW","ThreeLetterISORegionName":"ZWE"}] \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/cultures.json b/MediaBrowser.Server.Implementations/Localization/cultures.json new file mode 100644 index 0000000000..9d98b664b8 --- /dev/null +++ b/MediaBrowser.Server.Implementations/Localization/cultures.json @@ -0,0 +1 @@ +[{"Name":"af","DisplayName":"Afrikaans","TwoLetterISOLanguageName":"af","ThreeLetterISOLanguageName":"afr"},{"Name":"sq","DisplayName":"Albanian","TwoLetterISOLanguageName":"sq","ThreeLetterISOLanguageName":"sqi"},{"Name":"gsw","DisplayName":"Alsatian","TwoLetterISOLanguageName":"gsw","ThreeLetterISOLanguageName":"gsw"},{"Name":"am","DisplayName":"Amharic","TwoLetterISOLanguageName":"am","ThreeLetterISOLanguageName":"amh"},{"Name":"ar","DisplayName":"Arabic","TwoLetterISOLanguageName":"ar","ThreeLetterISOLanguageName":"ara"},{"Name":"hy","DisplayName":"Armenian","TwoLetterISOLanguageName":"hy","ThreeLetterISOLanguageName":"hye"},{"Name":"as","DisplayName":"Assamese","TwoLetterISOLanguageName":"as","ThreeLetterISOLanguageName":"asm"},{"Name":"az","DisplayName":"Azeri","TwoLetterISOLanguageName":"az","ThreeLetterISOLanguageName":"aze"},{"Name":"jv","DisplayName":"Basa Jawa","TwoLetterISOLanguageName":"jv","ThreeLetterISOLanguageName":"jav"},{"Name":"ba","DisplayName":"Bashkir","TwoLetterISOLanguageName":"ba","ThreeLetterISOLanguageName":"bak"},{"Name":"eu","DisplayName":"Basque","TwoLetterISOLanguageName":"eu","ThreeLetterISOLanguageName":"eus"},{"Name":"be","DisplayName":"Belarusian","TwoLetterISOLanguageName":"be","ThreeLetterISOLanguageName":"bel"},{"Name":"bn","DisplayName":"Bengali","TwoLetterISOLanguageName":"bn","ThreeLetterISOLanguageName":"bng"},{"Name":"bs","DisplayName":"Bosnian","TwoLetterISOLanguageName":"bs","ThreeLetterISOLanguageName":"bsb"},{"Name":"bs-Cyrl","DisplayName":"Bosnian (Cyrillic)","TwoLetterISOLanguageName":"bs","ThreeLetterISOLanguageName":"bsc"},{"Name":"br","DisplayName":"Breton","TwoLetterISOLanguageName":"br","ThreeLetterISOLanguageName":"bre"},{"Name":"bg","DisplayName":"Bulgarian","TwoLetterISOLanguageName":"bg","ThreeLetterISOLanguageName":"bul"},{"Name":"my","DisplayName":"Burmese","TwoLetterISOLanguageName":"my","ThreeLetterISOLanguageName":"mya"},{"Name":"ca","DisplayName":"Catalan","TwoLetterISOLanguageName":"ca","ThreeLetterISOLanguageName":"cat"},{"Name":"tzm-Tfng-MA","DisplayName":"Central Atlas Tamazight (Tifinagh, Morocco)","TwoLetterISOLanguageName":"tzm","ThreeLetterISOLanguageName":"tzm"},{"Name":"ku","DisplayName":"Central Kurdish","TwoLetterISOLanguageName":"ku","ThreeLetterISOLanguageName":"kur"},{"Name":"chr","DisplayName":"Cherokee","TwoLetterISOLanguageName":"chr","ThreeLetterISOLanguageName":"chr"},{"Name":"zh","DisplayName":"Chinese","TwoLetterISOLanguageName":"zh","ThreeLetterISOLanguageName":"zho"},{"Name":"sn","DisplayName":"chiShona","TwoLetterISOLanguageName":"sn","ThreeLetterISOLanguageName":"sna"},{"Name":"co","DisplayName":"Corsican","TwoLetterISOLanguageName":"co","ThreeLetterISOLanguageName":"cos"},{"Name":"hr","DisplayName":"Croatian","TwoLetterISOLanguageName":"hr","ThreeLetterISOLanguageName":"hrv"},{"Name":"hr-BA","DisplayName":"Croatian (Latin, Bosnia and Herzegovina)","TwoLetterISOLanguageName":"hr","ThreeLetterISOLanguageName":"hrb"},{"Name":"cs","DisplayName":"Czech","TwoLetterISOLanguageName":"cs","ThreeLetterISOLanguageName":"ces"},{"Name":"da","DisplayName":"Danish","TwoLetterISOLanguageName":"da","ThreeLetterISOLanguageName":"dan"},{"Name":"prs","DisplayName":"Dari","TwoLetterISOLanguageName":"prs","ThreeLetterISOLanguageName":"prs"},{"Name":"dv","DisplayName":"Divehi","TwoLetterISOLanguageName":"dv","ThreeLetterISOLanguageName":"div"},{"Name":"nl","DisplayName":"Dutch","TwoLetterISOLanguageName":"nl","ThreeLetterISOLanguageName":"nld"},{"Name":"en","DisplayName":"English","TwoLetterISOLanguageName":"en","ThreeLetterISOLanguageName":"eng"},{"Name":"et","DisplayName":"Estonian","TwoLetterISOLanguageName":"et","ThreeLetterISOLanguageName":"est"},{"Name":"fo","DisplayName":"Faroese","TwoLetterISOLanguageName":"fo","ThreeLetterISOLanguageName":"fao"},{"Name":"fil","DisplayName":"Filipino","TwoLetterISOLanguageName":"fil","ThreeLetterISOLanguageName":"fil"},{"Name":"fi","DisplayName":"Finnish","TwoLetterISOLanguageName":"fi","ThreeLetterISOLanguageName":"fin"},{"Name":"fr","DisplayName":"French","TwoLetterISOLanguageName":"fr","ThreeLetterISOLanguageName":"fra"},{"Name":"fy","DisplayName":"Frisian","TwoLetterISOLanguageName":"fy","ThreeLetterISOLanguageName":"fry"},{"Name":"ff","DisplayName":"Fulah","TwoLetterISOLanguageName":"ff","ThreeLetterISOLanguageName":"ful"},{"Name":"gl","DisplayName":"Galician","TwoLetterISOLanguageName":"gl","ThreeLetterISOLanguageName":"glg"},{"Name":"ka","DisplayName":"Georgian","TwoLetterISOLanguageName":"ka","ThreeLetterISOLanguageName":"kat"},{"Name":"de","DisplayName":"German","TwoLetterISOLanguageName":"de","ThreeLetterISOLanguageName":"deu"},{"Name":"el","DisplayName":"Greek","TwoLetterISOLanguageName":"el","ThreeLetterISOLanguageName":"ell"},{"Name":"kl","DisplayName":"Greenlandic","TwoLetterISOLanguageName":"kl","ThreeLetterISOLanguageName":"kal"},{"Name":"gn","DisplayName":"Guarani","TwoLetterISOLanguageName":"gn","ThreeLetterISOLanguageName":"grn"},{"Name":"gu","DisplayName":"Gujarati","TwoLetterISOLanguageName":"gu","ThreeLetterISOLanguageName":"guj"},{"Name":"ha","DisplayName":"Hausa","TwoLetterISOLanguageName":"ha","ThreeLetterISOLanguageName":"hau"},{"Name":"haw","DisplayName":"Hawaiian","TwoLetterISOLanguageName":"haw","ThreeLetterISOLanguageName":"haw"},{"Name":"he","DisplayName":"Hebrew","TwoLetterISOLanguageName":"he","ThreeLetterISOLanguageName":"heb"},{"Name":"hi","DisplayName":"Hindi","TwoLetterISOLanguageName":"hi","ThreeLetterISOLanguageName":"hin"},{"Name":"hu","DisplayName":"Hungarian","TwoLetterISOLanguageName":"hu","ThreeLetterISOLanguageName":"hun"},{"Name":"is","DisplayName":"Icelandic","TwoLetterISOLanguageName":"is","ThreeLetterISOLanguageName":"isl"},{"Name":"ig","DisplayName":"Igbo","TwoLetterISOLanguageName":"ig","ThreeLetterISOLanguageName":"ibo"},{"Name":"id","DisplayName":"Indonesian","TwoLetterISOLanguageName":"id","ThreeLetterISOLanguageName":"ind"},{"Name":"iu","DisplayName":"Inuktitut","TwoLetterISOLanguageName":"iu","ThreeLetterISOLanguageName":"iku"},{"Name":"","DisplayName":"Invariant Language (Invariant Country)","TwoLetterISOLanguageName":"iv","ThreeLetterISOLanguageName":"ivl"},{"Name":"ga","DisplayName":"Irish","TwoLetterISOLanguageName":"ga","ThreeLetterISOLanguageName":"gle"},{"Name":"xh","DisplayName":"isiXhosa","TwoLetterISOLanguageName":"xh","ThreeLetterISOLanguageName":"xho"},{"Name":"zu","DisplayName":"isiZulu","TwoLetterISOLanguageName":"zu","ThreeLetterISOLanguageName":"zul"},{"Name":"it","DisplayName":"Italian","TwoLetterISOLanguageName":"it","ThreeLetterISOLanguageName":"ita"},{"Name":"ja","DisplayName":"Japanese","TwoLetterISOLanguageName":"ja","ThreeLetterISOLanguageName":"jpn"},{"Name":"kn","DisplayName":"Kannada","TwoLetterISOLanguageName":"kn","ThreeLetterISOLanguageName":"kan"},{"Name":"kk","DisplayName":"Kazakh","TwoLetterISOLanguageName":"kk","ThreeLetterISOLanguageName":"kaz"},{"Name":"km","DisplayName":"Khmer","TwoLetterISOLanguageName":"km","ThreeLetterISOLanguageName":"khm"},{"Name":"qut","DisplayName":"K'iche","TwoLetterISOLanguageName":"qut","ThreeLetterISOLanguageName":"qut"},{"Name":"rw","DisplayName":"Kinyarwanda","TwoLetterISOLanguageName":"rw","ThreeLetterISOLanguageName":"kin"},{"Name":"sw","DisplayName":"Kiswahili","TwoLetterISOLanguageName":"sw","ThreeLetterISOLanguageName":"swa"},{"Name":"kok","DisplayName":"Konkani","TwoLetterISOLanguageName":"kok","ThreeLetterISOLanguageName":"kok"},{"Name":"ko","DisplayName":"Korean","TwoLetterISOLanguageName":"ko","ThreeLetterISOLanguageName":"kor"},{"Name":"ky","DisplayName":"Kyrgyz","TwoLetterISOLanguageName":"ky","ThreeLetterISOLanguageName":"kir"},{"Name":"lo","DisplayName":"Lao","TwoLetterISOLanguageName":"lo","ThreeLetterISOLanguageName":"lao"},{"Name":"lv","DisplayName":"Latvian","TwoLetterISOLanguageName":"lv","ThreeLetterISOLanguageName":"lav"},{"Name":"lt","DisplayName":"Lithuanian","TwoLetterISOLanguageName":"lt","ThreeLetterISOLanguageName":"lit"},{"Name":"dsb","DisplayName":"Lower Sorbian","TwoLetterISOLanguageName":"dsb","ThreeLetterISOLanguageName":"dsb"},{"Name":"lb","DisplayName":"Luxembourgish","TwoLetterISOLanguageName":"lb","ThreeLetterISOLanguageName":"ltz"},{"Name":"mk-MK","DisplayName":"Macedonian (Former Yugoslav Republic of Macedonia)","TwoLetterISOLanguageName":"mk","ThreeLetterISOLanguageName":"mkd"},{"Name":"mg","DisplayName":"Malagasy","TwoLetterISOLanguageName":"mg","ThreeLetterISOLanguageName":"mlg"},{"Name":"ms","DisplayName":"Malay","TwoLetterISOLanguageName":"ms","ThreeLetterISOLanguageName":"msa"},{"Name":"ml","DisplayName":"Malayalam","TwoLetterISOLanguageName":"ml","ThreeLetterISOLanguageName":"mym"},{"Name":"mt","DisplayName":"Maltese","TwoLetterISOLanguageName":"mt","ThreeLetterISOLanguageName":"mlt"},{"Name":"mi","DisplayName":"Maori","TwoLetterISOLanguageName":"mi","ThreeLetterISOLanguageName":"mri"},{"Name":"arn","DisplayName":"Mapudungun","TwoLetterISOLanguageName":"arn","ThreeLetterISOLanguageName":"arn"},{"Name":"mr","DisplayName":"Marathi","TwoLetterISOLanguageName":"mr","ThreeLetterISOLanguageName":"mar"},{"Name":"moh","DisplayName":"Mohawk","TwoLetterISOLanguageName":"moh","ThreeLetterISOLanguageName":"moh"},{"Name":"mn","DisplayName":"Mongolian","TwoLetterISOLanguageName":"mn","ThreeLetterISOLanguageName":"mon"},{"Name":"ne","DisplayName":"Nepali","TwoLetterISOLanguageName":"ne","ThreeLetterISOLanguageName":"nep"},{"Name":"no","DisplayName":"Norwegian","TwoLetterISOLanguageName":"nb","ThreeLetterISOLanguageName":"nob"},{"Name":"nn","DisplayName":"Norwegian (Nynorsk)","TwoLetterISOLanguageName":"nn","ThreeLetterISOLanguageName":"nno"},{"Name":"oc","DisplayName":"Occitan","TwoLetterISOLanguageName":"oc","ThreeLetterISOLanguageName":"oci"},{"Name":"or","DisplayName":"Oriya","TwoLetterISOLanguageName":"or","ThreeLetterISOLanguageName":"ori"},{"Name":"om","DisplayName":"Oromo","TwoLetterISOLanguageName":"om","ThreeLetterISOLanguageName":"orm"},{"Name":"ps","DisplayName":"Pashto","TwoLetterISOLanguageName":"ps","ThreeLetterISOLanguageName":"pus"},{"Name":"fa","DisplayName":"Persian","TwoLetterISOLanguageName":"fa","ThreeLetterISOLanguageName":"fas"},{"Name":"pl","DisplayName":"Polish","TwoLetterISOLanguageName":"pl","ThreeLetterISOLanguageName":"pol"},{"Name":"pt-AO","DisplayName":"português (Angola)","TwoLetterISOLanguageName":"pt","ThreeLetterISOLanguageName":"por"},{"Name":"pa","DisplayName":"Punjabi","TwoLetterISOLanguageName":"pa","ThreeLetterISOLanguageName":"pan"},{"Name":"quz","DisplayName":"Quechua","TwoLetterISOLanguageName":"quz","ThreeLetterISOLanguageName":"qub"},{"Name":"quz-EC","DisplayName":"Quechua (Ecuador)","TwoLetterISOLanguageName":"quz","ThreeLetterISOLanguageName":"que"},{"Name":"quz-PE","DisplayName":"Quechua (Peru)","TwoLetterISOLanguageName":"quz","ThreeLetterISOLanguageName":"qup"},{"Name":"ro","DisplayName":"Romanian","TwoLetterISOLanguageName":"ro","ThreeLetterISOLanguageName":"ron"},{"Name":"rm","DisplayName":"Romansh","TwoLetterISOLanguageName":"rm","ThreeLetterISOLanguageName":"roh"},{"Name":"ru","DisplayName":"Russian","TwoLetterISOLanguageName":"ru","ThreeLetterISOLanguageName":"rus"},{"Name":"sah","DisplayName":"Sakha","TwoLetterISOLanguageName":"sah","ThreeLetterISOLanguageName":"sah"},{"Name":"smn","DisplayName":"Sami (Inari)","TwoLetterISOLanguageName":"smn","ThreeLetterISOLanguageName":"smn"},{"Name":"smj","DisplayName":"Sami (Lule)","TwoLetterISOLanguageName":"smj","ThreeLetterISOLanguageName":"smj"},{"Name":"se","DisplayName":"Sami (Northern)","TwoLetterISOLanguageName":"se","ThreeLetterISOLanguageName":"sme"},{"Name":"sms","DisplayName":"Sami (Skolt)","TwoLetterISOLanguageName":"sms","ThreeLetterISOLanguageName":"sms"},{"Name":"sma","DisplayName":"Sami (Southern)","TwoLetterISOLanguageName":"sma","ThreeLetterISOLanguageName":"sma"},{"Name":"sa","DisplayName":"Sanskrit","TwoLetterISOLanguageName":"sa","ThreeLetterISOLanguageName":"san"},{"Name":"gd","DisplayName":"Scottish Gaelic","TwoLetterISOLanguageName":"gd","ThreeLetterISOLanguageName":"gla"},{"Name":"sr","DisplayName":"Serbian","TwoLetterISOLanguageName":"sr","ThreeLetterISOLanguageName":"srp"},{"Name":"sr-Cyrl-BA","DisplayName":"Serbian (Cyrillic, Bosnia and Herzegovina)","TwoLetterISOLanguageName":"sr","ThreeLetterISOLanguageName":"srn"},{"Name":"sr-Latn-BA","DisplayName":"Serbian (Latin, Bosnia and Herzegovina)","TwoLetterISOLanguageName":"sr","ThreeLetterISOLanguageName":"srs"},{"Name":"nso","DisplayName":"Sesotho sa Leboa","TwoLetterISOLanguageName":"nso","ThreeLetterISOLanguageName":"nso"},{"Name":"tn","DisplayName":"Setswana","TwoLetterISOLanguageName":"tn","ThreeLetterISOLanguageName":"tsn"},{"Name":"sd","DisplayName":"Sindhi","TwoLetterISOLanguageName":"sd","ThreeLetterISOLanguageName":"sin"},{"Name":"si","DisplayName":"Sinhala","TwoLetterISOLanguageName":"si","ThreeLetterISOLanguageName":"sin"},{"Name":"sk","DisplayName":"Slovak","TwoLetterISOLanguageName":"sk","ThreeLetterISOLanguageName":"slk"},{"Name":"sl","DisplayName":"Slovenian","TwoLetterISOLanguageName":"sl","ThreeLetterISOLanguageName":"slv"},{"Name":"so","DisplayName":"Somali","TwoLetterISOLanguageName":"so","ThreeLetterISOLanguageName":"som"},{"Name":"st","DisplayName":"Southern Sotho","TwoLetterISOLanguageName":"st","ThreeLetterISOLanguageName":"sot"},{"Name":"es","DisplayName":"Spanish","TwoLetterISOLanguageName":"es","ThreeLetterISOLanguageName":"spa"},{"Name":"zgh","DisplayName":"Standard Morrocan Tamazight","TwoLetterISOLanguageName":"zgh","ThreeLetterISOLanguageName":"zgh"},{"Name":"sv","DisplayName":"Swedish","TwoLetterISOLanguageName":"sv","ThreeLetterISOLanguageName":"swe"},{"Name":"syr","DisplayName":"Syriac","TwoLetterISOLanguageName":"syr","ThreeLetterISOLanguageName":"syr"},{"Name":"tg","DisplayName":"Tajik","TwoLetterISOLanguageName":"tg","ThreeLetterISOLanguageName":"tgk"},{"Name":"ta","DisplayName":"Tamil","TwoLetterISOLanguageName":"ta","ThreeLetterISOLanguageName":"tam"},{"Name":"tt","DisplayName":"Tatar","TwoLetterISOLanguageName":"tt","ThreeLetterISOLanguageName":"tat"},{"Name":"te","DisplayName":"Telugu","TwoLetterISOLanguageName":"te","ThreeLetterISOLanguageName":"tel"},{"Name":"th","DisplayName":"Thai","TwoLetterISOLanguageName":"th","ThreeLetterISOLanguageName":"tha"},{"Name":"bo","DisplayName":"Tibetan","TwoLetterISOLanguageName":"bo","ThreeLetterISOLanguageName":"bod"},{"Name":"ti","DisplayName":"Tigrinya","TwoLetterISOLanguageName":"ti","ThreeLetterISOLanguageName":"tir"},{"Name":"ts","DisplayName":"Tsonga","TwoLetterISOLanguageName":"ts","ThreeLetterISOLanguageName":"tso"},{"Name":"tr","DisplayName":"Turkish","TwoLetterISOLanguageName":"tr","ThreeLetterISOLanguageName":"tur"},{"Name":"tk","DisplayName":"Turkmen","TwoLetterISOLanguageName":"tk","ThreeLetterISOLanguageName":"tuk"},{"Name":"uk","DisplayName":"Ukrainian","TwoLetterISOLanguageName":"uk","ThreeLetterISOLanguageName":"ukr"},{"Name":"hsb","DisplayName":"Upper Sorbian","TwoLetterISOLanguageName":"hsb","ThreeLetterISOLanguageName":"hsb"},{"Name":"ur","DisplayName":"Urdu","TwoLetterISOLanguageName":"ur","ThreeLetterISOLanguageName":"urd"},{"Name":"ug","DisplayName":"Uyghur","TwoLetterISOLanguageName":"ug","ThreeLetterISOLanguageName":"uig"},{"Name":"uz","DisplayName":"Uzbek","TwoLetterISOLanguageName":"uz","ThreeLetterISOLanguageName":"uzb"},{"Name":"vi","DisplayName":"Vietnamese","TwoLetterISOLanguageName":"vi","ThreeLetterISOLanguageName":"vie"},{"Name":"cy","DisplayName":"Welsh","TwoLetterISOLanguageName":"cy","ThreeLetterISOLanguageName":"cym"},{"Name":"wo","DisplayName":"Wolof","TwoLetterISOLanguageName":"wo","ThreeLetterISOLanguageName":"wol"},{"Name":"ii","DisplayName":"Yi","TwoLetterISOLanguageName":"ii","ThreeLetterISOLanguageName":"iii"},{"Name":"yo","DisplayName":"Yoruba","TwoLetterISOLanguageName":"yo","ThreeLetterISOLanguageName":"yor"}] \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 6e5e58d26c..3532ee3703 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -169,7 +169,6 @@ - @@ -328,6 +327,8 @@ + + diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index af400f8502..c79d84e5a7 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -31,11 +31,11 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Resolvers; using MediaBrowser.Controller.Session; using MediaBrowser.Controller.Sorting; +using MediaBrowser.Controller.Subtitles; using MediaBrowser.Controller.Themes; using MediaBrowser.Dlna; using MediaBrowser.Dlna.Eventing; using MediaBrowser.Dlna.Main; -using MediaBrowser.Dlna.PlayTo; using MediaBrowser.Dlna.Server; using MediaBrowser.MediaEncoding.BdInfo; using MediaBrowser.MediaEncoding.Encoder; @@ -44,6 +44,7 @@ using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.System; using MediaBrowser.Model.Updates; using MediaBrowser.Providers.Manager; +using MediaBrowser.Providers.Subtitles; using MediaBrowser.Server.Implementations; using MediaBrowser.Server.Implementations.Channels; using MediaBrowser.Server.Implementations.Collections; @@ -193,6 +194,7 @@ namespace MediaBrowser.ServerApplication private IProviderRepository ProviderRepository { get; set; } private INotificationManager NotificationManager { get; set; } + private ISubtitleManager SubtitleManager { get; set; } /// /// Initializes a new instance of the class. @@ -531,6 +533,9 @@ namespace MediaBrowser.ServerApplication NotificationManager = new NotificationManager(LogManager, UserManager, ServerConfigurationManager); RegisterSingleInstance(NotificationManager); + SubtitleManager = new SubtitleManager(LogManager.GetLogger("SubtitleManager"), FileSystemManager, LibraryMonitor); + RegisterSingleInstance(SubtitleManager); + var displayPreferencesTask = Task.Run(async () => await ConfigureDisplayPreferencesRepositories().ConfigureAwait(false)); var itemsTask = Task.Run(async () => await ConfigureItemRepositories().ConfigureAwait(false)); var userdataTask = Task.Run(async () => await ConfigureUserDataRepositories().ConfigureAwait(false)); @@ -566,7 +571,7 @@ namespace MediaBrowser.ServerApplication { var info = await new FFMpegDownloader(Logger, ApplicationPaths, HttpClient, ZipClient, FileSystemManager).GetFFMpegInfo(progress).ConfigureAwait(false); - MediaEncoder = new MediaEncoder(LogManager.GetLogger("MediaEncoder"), ApplicationPaths, JsonSerializer, info.Path, info.ProbePath, info.Version, FileSystemManager); + MediaEncoder = new MediaEncoder(LogManager.GetLogger("MediaEncoder"), ApplicationPaths, JsonSerializer, info.EncoderPath, info.ProbePath, info.Version, FileSystemManager); RegisterSingleInstance(MediaEncoder); } @@ -710,6 +715,8 @@ namespace MediaBrowser.ServerApplication LiveTvManager.AddParts(GetExports()); + SubtitleManager.AddParts(GetExports()); + SessionManager.AddParts(GetExports()); ChannelManager.AddParts(GetExports(), GetExports()); diff --git a/MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloadInfo.cs b/MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloadInfo.cs index c4f5297545..19aa7a6843 100644 --- a/MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloadInfo.cs +++ b/MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloadInfo.cs @@ -4,6 +4,8 @@ using Mono.Unix.Native; using System.Text.RegularExpressions; using System.IO; #endif +using System.IO; +using System.Text.RegularExpressions; namespace MediaBrowser.ServerApplication.FFMpeg { @@ -32,7 +34,7 @@ namespace MediaBrowser.ServerApplication.FFMpeg switch (arg) { case "Version": - return "20140304"; + return "20140506"; case "FFMpegFilename": return "ffmpeg.exe"; case "FFProbeFilename": @@ -42,7 +44,6 @@ namespace MediaBrowser.ServerApplication.FFMpeg } break; - #if __MonoCS__ case PlatformID.Unix: if (PlatformDetection.IsMac) { @@ -69,7 +70,7 @@ namespace MediaBrowser.ServerApplication.FFMpeg switch (arg) { case "Version": - return "20140304"; + return "20140506"; case "FFMpegFilename": return "ffmpeg"; case "FFProbeFilename": @@ -85,7 +86,7 @@ namespace MediaBrowser.ServerApplication.FFMpeg switch (arg) { case "Version": - return "20140304"; + return "20140505"; case "FFMpegFilename": return "ffmpeg"; case "FFProbeFilename": @@ -98,7 +99,6 @@ namespace MediaBrowser.ServerApplication.FFMpeg } // Unsupported Unix platform return ""; -#endif } return ""; } @@ -106,18 +106,17 @@ namespace MediaBrowser.ServerApplication.FFMpeg private static string[] GetDownloadUrls() { var pid = Environment.OSVersion.Platform; - + switch (pid) { case PlatformID.Win32NT: return new[] { - "http://ffmpeg.zeranoe.com/builds/win32/static/ffmpeg-20140304-git-f34cceb-win32-static.7z", - "https://www.dropbox.com/s/6brdetuzbld93jk/ffmpeg-20140304-git-f34cceb-win32-static.7z?dl=1" + "http://ffmpeg.zeranoe.com/builds/win32/static/ffmpeg-20140506-git-2baf1c8-win32-static.7z", + "https://www.dropbox.com/s/lxlzxs0r83iatsv/ffmpeg-20140506-git-2baf1c8-win32-static.7z?dl=1" }; - - #if __MonoCS__ - case PlatformID.Unix: + + case PlatformID.Unix: if (PlatformDetection.IsMac && PlatformDetection.IsX86_64) { return new[] @@ -132,8 +131,8 @@ namespace MediaBrowser.ServerApplication.FFMpeg { return new[] { - "http://ffmpeg.gusari.org/static/32bit/ffmpeg.static.32bit.2014-03-04.tar.gz", - "https://www.dropbox.com/s/0l76mcauqqkta31/ffmpeg.static.32bit.2014-03-04.tar.gz?dl=1" + "http://ffmpeg.gusari.org/static/32bit/ffmpeg.static.32bit.latest.tar.gz", + "https://www.dropbox.com/s/k9s02pv5to6slfb/ffmpeg.static.32bit.2014-05-06.tar.gz?dl=1" }; } @@ -141,22 +140,20 @@ namespace MediaBrowser.ServerApplication.FFMpeg { return new[] { - "http://ffmpeg.gusari.org/static/64bit/ffmpeg.static.64bit.2014-03-04.tar.gz", - "https://www.dropbox.com/s/9wlxz440mdejuqe/ffmpeg.static.64bit.2014-03-04.tar.gz?dl=1" + "http://ffmpeg.gusari.org/static/64bit/ffmpeg.static.64bit.latest.tar.gz", + "https://www.dropbox.com/s/onuregwghywnzjo/ffmpeg.static.64bit.2014-05-05.tar.gz?dl=1" }; } } //No Unix version available - return new string[] {}; -#endif + return new string[] { }; } - return new string[] {}; + return new string[] { }; } } - #if __MonoCS__ public static class PlatformDetection { public readonly static bool IsWindows; @@ -166,34 +163,52 @@ namespace MediaBrowser.ServerApplication.FFMpeg public readonly static bool IsX86_64; public readonly static bool IsArm; - static PlatformDetection () + static PlatformDetection() { IsWindows = Path.DirectorySeparatorChar == '\\'; //Don't call uname on windows if (!IsWindows) { - Utsname uname; - var callResult = Syscall.uname(out uname); - if (callResult == 0) - { - IsMac = uname.sysname == "Darwin"; - IsLinux = !IsMac && uname.sysname == "Linux"; + var uname = GetUnixName(); - Regex archX86 = new Regex("(i|I)[3-6]86"); - IsX86 = archX86.IsMatch(uname.machine); - IsX86_64 = !IsX86 && uname.machine == "x86_64"; - IsArm = !IsX86 && !IsX86 && uname.machine.StartsWith("arm"); - } + IsMac = uname.sysname == "Darwin"; + IsLinux = uname.sysname == "Linux"; + + var archX86 = new Regex("(i|I)[3-6]86"); + IsX86 = archX86.IsMatch(uname.machine); + IsX86_64 = !IsX86 && uname.machine == "x86_64"; + IsArm = !IsX86 && !IsX86_64 && uname.machine.StartsWith("arm"); } else { - if (System.Environment.Is64BitOperatingSystem) + if (Environment.Is64BitOperatingSystem) IsX86_64 = true; else IsX86 = true; } } + + private static Uname GetUnixName() + { + var uname = new Uname(); + +#if __MonoCS__ + Utsname uname; + var callResult = Syscall.uname(out uname); + if (callResult == 0) + { + uname.sysname= uname.sysname; + uname.machine= uname.machine; + } +#endif + return uname; + } + } + + public class Uname + { + public string sysname = string.Empty; + public string machine = string.Empty; } - #endif } diff --git a/MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloader.cs b/MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloader.cs index b9c45e0d9b..c550cb27fb 100644 --- a/MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloader.cs +++ b/MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloader.cs @@ -42,63 +42,86 @@ namespace MediaBrowser.ServerApplication.FFMpeg public async Task GetFFMpegInfo(IProgress progress) { - var versionedDirectoryPath = Path.Combine(GetMediaToolsPath(true), FFMpegDownloadInfo.Version); + var rootEncoderPath = Path.Combine(_appPaths.ProgramDataPath, "ffmpeg"); + var versionedDirectoryPath = Path.Combine(rootEncoderPath, FFMpegDownloadInfo.Version); var info = new FFMpegInfo { ProbePath = Path.Combine(versionedDirectoryPath, FFMpegDownloadInfo.FFProbeFilename), - Path = Path.Combine(versionedDirectoryPath, FFMpegDownloadInfo.FFMpegFilename), + EncoderPath = Path.Combine(versionedDirectoryPath, FFMpegDownloadInfo.FFMpegFilename), Version = FFMpegDownloadInfo.Version }; Directory.CreateDirectory(versionedDirectoryPath); - var tasks = new List(); - - double ffmpegPercent = 0; - double fontPercent = 0; - var syncLock = new object(); - - if (!File.Exists(info.ProbePath) || !File.Exists(info.Path)) + if (!File.Exists(info.ProbePath) || !File.Exists(info.EncoderPath)) { - var ffmpegProgress = new ActionableProgress(); - ffmpegProgress.RegisterAction(p => - { - ffmpegPercent = p; + // ffmpeg not present. See if there's an older version we can start with + var existingVersion = GetExistingVersion(info, rootEncoderPath); - lock (syncLock) - { - progress.Report((ffmpegPercent / 2) + (fontPercent / 2)); - } - }); + // No older version. Need to download and block until complete + if (existingVersion == null) + { + await DownloadFFMpeg(versionedDirectoryPath, progress).ConfigureAwait(false); + } + else + { + // Older version found. + // Start with that. Download new version in the background. + var newPath = versionedDirectoryPath; + Task.Run(() => DownloadFFMpegInBackground(newPath)); - tasks.Add(DownloadFFMpeg(info, ffmpegProgress)); - } - else - { - ffmpegPercent = 100; - progress.Report(50); + info = existingVersion; + versionedDirectoryPath = Path.GetDirectoryName(info.EncoderPath); + } } - var fontProgress = new ActionableProgress(); - fontProgress.RegisterAction(p => + await DownloadFonts(versionedDirectoryPath).ConfigureAwait(false); + + return info; + } + + private FFMpegInfo GetExistingVersion(FFMpegInfo info, string rootEncoderPath) + { + var encoderFilename = Path.GetFileName(info.EncoderPath); + var probeFilename = Path.GetFileName(info.ProbePath); + + foreach (var directory in Directory.EnumerateDirectories(rootEncoderPath, "*", SearchOption.TopDirectoryOnly) + .ToList()) { - fontPercent = p; + var allFiles = Directory.EnumerateFiles(directory, "*", SearchOption.AllDirectories).ToList(); - lock (syncLock) + var encoder = allFiles.FirstOrDefault(i => string.Equals(Path.GetFileName(i), encoderFilename, StringComparison.OrdinalIgnoreCase)); + var probe = allFiles.FirstOrDefault(i => string.Equals(Path.GetFileName(i), probeFilename, StringComparison.OrdinalIgnoreCase)); + + if (!string.IsNullOrWhiteSpace(encoder) && + !string.IsNullOrWhiteSpace(probe)) { - progress.Report((ffmpegPercent / 2) + (fontPercent / 2)); + return new FFMpegInfo + { + EncoderPath = encoder, + ProbePath = probe, + Version = Path.GetFileNameWithoutExtension(Path.GetDirectoryName(probe)) + }; } - }); - - tasks.Add(DownloadFonts(versionedDirectoryPath, fontProgress)); + } - await Task.WhenAll(tasks).ConfigureAwait(false); + return null; + } - return info; + private async void DownloadFFMpegInBackground(string directory) + { + try + { + await DownloadFFMpeg(directory, new Progress()).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error downloading ffmpeg", ex); + } } - private async Task DownloadFFMpeg(FFMpegInfo info, IProgress progress) + private async Task DownloadFFMpeg(string directory, IProgress progress) { foreach (var url in FFMpegDownloadInfo.FfMpegUrls) { @@ -114,7 +137,7 @@ namespace MediaBrowser.ServerApplication.FFMpeg }).ConfigureAwait(false); - ExtractFFMpeg(tempFile, Path.GetDirectoryName(info.Path)); + ExtractFFMpeg(tempFile, directory); return; } catch (HttpException ex) @@ -132,7 +155,7 @@ namespace MediaBrowser.ServerApplication.FFMpeg private void ExtractFFMpeg(string tempFile, string targetFolder) { - _logger.Debug("Extracting ffmpeg from {0}", tempFile); + _logger.Info("Extracting ffmpeg from {0}", tempFile); var tempFolder = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid().ToString()); @@ -171,6 +194,8 @@ namespace MediaBrowser.ServerApplication.FFMpeg private void ExtractArchive(string archivePath, string targetPath) { + _logger.Info("Extracting {0} to {1}", archivePath, targetPath); + if (string.Equals(FFMpegDownloadInfo.ArchiveType, "7z", StringComparison.OrdinalIgnoreCase)) { _zipClient.ExtractAllFrom7z(archivePath, targetPath, true); @@ -182,6 +207,8 @@ namespace MediaBrowser.ServerApplication.FFMpeg } private void Extract7zArchive(string archivePath, string targetPath) { + _logger.Info("Extracting {0} to {1}", archivePath, targetPath); + _zipClient.ExtractAllFrom7z(archivePath, targetPath, true); } @@ -201,7 +228,8 @@ namespace MediaBrowser.ServerApplication.FFMpeg /// Extracts the fonts. /// /// The target path. - private async Task DownloadFonts(string targetPath, IProgress progress) + /// Task. + private async Task DownloadFonts(string targetPath) { try { @@ -213,12 +241,19 @@ namespace MediaBrowser.ServerApplication.FFMpeg var fontFile = Path.Combine(fontsDirectory, fontFilename); - if (!File.Exists(fontFile)) + if (File.Exists(fontFile)) { - await DownloadFontFile(fontsDirectory, fontFilename, progress).ConfigureAwait(false); + await WriteFontConfigFile(fontsDirectory).ConfigureAwait(false); + } + else + { + // Kick this off, but no need to wait on it + Task.Run(async () => + { + await DownloadFontFile(fontsDirectory, fontFilename, new Progress()).ConfigureAwait(false); + await WriteFontConfigFile(fontsDirectory).ConfigureAwait(false); + }); } - - await WriteFontConfigFile(fontsDirectory).ConfigureAwait(false); } catch (HttpException ex) { @@ -230,8 +265,6 @@ namespace MediaBrowser.ServerApplication.FFMpeg // Don't let the server crash because of this _logger.ErrorException("Error writing ffmpeg font files", ex); } - - progress.Report(100); } /// @@ -325,19 +358,5 @@ namespace MediaBrowser.ServerApplication.FFMpeg } } } - - /// - /// Gets the media tools path. - /// - /// if set to true [create]. - /// System.String. - private string GetMediaToolsPath(bool create) - { - var path = Path.Combine(_appPaths.ProgramDataPath, "ffmpeg"); - - Directory.CreateDirectory(path); - - return path; - } } } diff --git a/MediaBrowser.ServerApplication/FFMpeg/FFMpegInfo.cs b/MediaBrowser.ServerApplication/FFMpeg/FFMpegInfo.cs index 147a9f7717..1361277aac 100644 --- a/MediaBrowser.ServerApplication/FFMpeg/FFMpegInfo.cs +++ b/MediaBrowser.ServerApplication/FFMpeg/FFMpegInfo.cs @@ -9,7 +9,7 @@ /// Gets or sets the path. /// /// The path. - public string Path { get; set; } + public string EncoderPath { get; set; } /// /// Gets or sets the probe path. /// diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index 4b9dad90a0..3072413f92 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -217,6 +217,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/OpenSubtitlesHandler/OpenSubtitles.cs b/OpenSubtitlesHandler/OpenSubtitles.cs index ba3c461a1b..5353586c85 100644 --- a/OpenSubtitlesHandler/OpenSubtitles.cs +++ b/OpenSubtitlesHandler/OpenSubtitles.cs @@ -20,6 +20,8 @@ using System; using System.Text; using System.Collections.Generic; using System.IO; +using System.Threading; +using System.Threading.Tasks; using OpenSubtitlesHandler.Console; using XmlRpcHandler; @@ -96,6 +98,56 @@ namespace OpenSubtitlesHandler } return new MethodResponseError("Fail", "Log in failed !"); } + + public static async Task LogInAsync(string userName, string password, string language, CancellationToken cancellationToken) + { + // Method call .. + List parms = new List(); + parms.Add(new XmlRpcValueBasic(userName)); + parms.Add(new XmlRpcValueBasic(password)); + parms.Add(new XmlRpcValueBasic(language)); + parms.Add(new XmlRpcValueBasic(XML_PRC_USERAGENT)); + XmlRpcMethodCall call = new XmlRpcMethodCall("LogIn", parms); + OSHConsole.WriteLine("Sending LogIn request to the server ...", DebugCode.Good); + + //File.WriteAllText(".\\request.txt", Encoding.UTF8.GetString(XmlRpcGenerator.Generate(call))); + // Send the request to the server + var stream = await Utilities.SendRequestAsync(XmlRpcGenerator.Generate(call), XML_PRC_USERAGENT, cancellationToken) + .ConfigureAwait(false); + + string response = Utilities.GetStreamString(stream); + + if (!response.Contains("ERROR:")) + { + // No error occur, get and decode the response. We expect Struct here. + XmlRpcMethodCall[] calls = XmlRpcGenerator.DecodeMethodResponse(response); + if (calls.Length > 0) + { + if (calls[0].Parameters.Count > 0) + { + XmlRpcValueStruct mainStruct = (XmlRpcValueStruct)calls[0].Parameters[0]; + MethodResponseLogIn re = new MethodResponseLogIn("Success", "Log in successful."); + foreach (XmlRpcStructMember MEMBER in mainStruct.Members) + { + switch (MEMBER.Name) + { + case "token": re.Token = TOKEN = MEMBER.Data.Data.ToString(); OSHConsole.WriteLine(MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "seconds": re.Seconds = (double)MEMBER.Data.Data; OSHConsole.WriteLine(MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + case "status": re.Status = MEMBER.Data.Data.ToString(); OSHConsole.WriteLine(MEMBER.Name + "= " + MEMBER.Data.Data.ToString()); break; + } + } + return re; + } + } + } + else + { + OSHConsole.WriteLine(response, DebugCode.Error); + return new MethodResponseError("Fail", response); + } + return new MethodResponseError("Fail", "Log in failed !"); + } + /// /// Log out from the server. Call this to terminate the session. /// diff --git a/OpenSubtitlesHandler/Utilities.cs b/OpenSubtitlesHandler/Utilities.cs index 5c72f4fde7..7f0f930094 100644 --- a/OpenSubtitlesHandler/Utilities.cs +++ b/OpenSubtitlesHandler/Utilities.cs @@ -24,6 +24,7 @@ using System.IO; using System.IO.Compression; using System.Net; using System.Security.Cryptography; +using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Net; @@ -161,7 +162,7 @@ namespace OpenSubtitlesHandler /// Response of the server or stream of error message as string started with 'ERROR:' keyword. public static Stream SendRequest(byte[] request, string userAgent) { - return SendRequestAsync(request, userAgent).Result; + return SendRequestAsync(request, userAgent, CancellationToken.None).Result; //HttpWebRequest req = (HttpWebRequest)WebRequest.Create(XML_RPC_SERVER); //req.ContentType = "text/xml"; @@ -190,16 +191,27 @@ namespace OpenSubtitlesHandler //} } - public static async Task SendRequestAsync(byte[] request, string userAgent) + public static async Task SendRequestAsync(byte[] request, string userAgent, CancellationToken cancellationToken) { var options = new HttpRequestOptions { RequestContentBytes = request, RequestContentType = "text/xml", - UserAgent = "xmlrpc-epi-php/0.2 (PHP)", - Url = XML_RPC_SERVER + UserAgent = userAgent, + Host = "api.opensubtitles.org:80", + Url = XML_RPC_SERVER, + + // Response parsing will fail with this enabled + EnableHttpCompression = false, + + CancellationToken = cancellationToken }; + if (string.IsNullOrEmpty(options.UserAgent)) + { + options.UserAgent = "xmlrpc-epi-php/0.2 (PHP)"; + } + var result = await HttpClient.Post(options).ConfigureAwait(false); return result.Content; -- cgit v1.2.3 From 9e4b34a4b1baebf611b615ead6018c15c4536820 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Wed, 7 May 2014 14:38:50 -0400 Subject: add basic open subtitle configuration --- .../Playback/Progressive/VideoService.cs | 10 +- .../Configuration/IServerConfigurationManager.cs | 7 ++ .../MediaBrowser.Controller.csproj | 1 + .../Security/IEncryptionManager.cs | 20 ++++ .../Configuration/ServerConfiguration.cs | 3 + .../Subtitles/OpenSubtitleDownloader.cs | 90 ++++++++++++++--- .../Configuration/ServerConfigurationManager.cs | 11 +- .../Localization/JavaScript/javascript.json | 2 - .../Localization/Server/server.json | 12 ++- .../MediaBrowser.Server.Implementations.csproj | 2 + .../Security/EncryptionManager.cs | 36 +++++++ MediaBrowser.ServerApplication/ApplicationHost.cs | 4 + MediaBrowser.WebDashboard/Api/DashboardService.cs | 2 +- .../MediaBrowser.WebDashboard.csproj | 16 ++- OpenSubtitlesHandler/OpenSubtitles.cs | 111 +++++++++++++++++++++ 15 files changed, 290 insertions(+), 37 deletions(-) create mode 100644 MediaBrowser.Controller/Security/IEncryptionManager.cs create mode 100644 MediaBrowser.Server.Implementations/Security/EncryptionManager.cs (limited to 'OpenSubtitlesHandler') diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index d61e8d73ad..f21e69290e 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs @@ -131,15 +131,15 @@ namespace MediaBrowser.Api.Playback.Progressive { var args = "-vcodec " + codec; - // See if we can save come cpu cycles by avoiding encoding - if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase)) + if (state.EnableMpegtsM2TsMode) { - return state.VideoStream != null && IsH264(state.VideoStream) ? args + " -bsf h264_mp4toannexb" : args; + args += " -mpegts_m2ts_mode 1"; } - if (state.EnableMpegtsM2TsMode) + // See if we can save come cpu cycles by avoiding encoding + if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase)) { - args += " -mpegts_m2ts_mode 1"; + return state.VideoStream != null && IsH264(state.VideoStream) ? args + " -bsf h264_mp4toannexb" : args; } const string keyFrameArg = " -force_key_frames expr:if(isnan(prev_forced_t),gte(t,.1),gte(t,prev_forced_t+5))"; diff --git a/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs b/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs index 810376f6c6..535e74feee 100644 --- a/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs +++ b/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs @@ -1,5 +1,7 @@ using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Events; using MediaBrowser.Model.Configuration; +using System; namespace MediaBrowser.Controller.Configuration { @@ -8,6 +10,11 @@ namespace MediaBrowser.Controller.Configuration /// public interface IServerConfigurationManager : IConfigurationManager { + /// + /// Occurs when [configuration updating]. + /// + event EventHandler> ConfigurationUpdating; + /// /// Gets the application paths. /// diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 6a3709dda6..6a7557e3a5 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -191,6 +191,7 @@ + diff --git a/MediaBrowser.Controller/Security/IEncryptionManager.cs b/MediaBrowser.Controller/Security/IEncryptionManager.cs new file mode 100644 index 0000000000..bb4f77d83b --- /dev/null +++ b/MediaBrowser.Controller/Security/IEncryptionManager.cs @@ -0,0 +1,20 @@ + +namespace MediaBrowser.Controller.Security +{ + public interface IEncryptionManager + { + /// + /// Encrypts the string. + /// + /// The value. + /// System.String. + string EncryptString(string value); + + /// + /// Decrypts the string. + /// + /// The value. + /// System.String. + string DecryptString(string value); + } +} diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 486268e2b6..32b1a7e015 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -323,6 +323,9 @@ namespace MediaBrowser.Model.Configuration public bool DownloadMovieSubtitles { get; set; } public bool DownloadEpisodeSubtitles { get; set; } + public string OpenSubtitlesUsername { get; set; } + public string OpenSubtitlesPasswordHash { get; set; } + public SubtitleOptions() { SubtitleDownloadLanguages = new string[] { }; diff --git a/MediaBrowser.Providers/Subtitles/OpenSubtitleDownloader.cs b/MediaBrowser.Providers/Subtitles/OpenSubtitleDownloader.cs index 929cccd5ab..f76528c3f4 100644 --- a/MediaBrowser.Providers/Subtitles/OpenSubtitleDownloader.cs +++ b/MediaBrowser.Providers/Subtitles/OpenSubtitleDownloader.cs @@ -1,7 +1,9 @@ -using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Events; using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Providers; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Security; using MediaBrowser.Controller.Subtitles; +using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Providers; @@ -16,16 +18,52 @@ using System.Threading.Tasks; namespace MediaBrowser.Providers.Subtitles { - public class OpenSubtitleDownloader : ISubtitleProvider + public class OpenSubtitleDownloader : ISubtitleProvider, IDisposable { private readonly ILogger _logger; private readonly IHttpClient _httpClient; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - public OpenSubtitleDownloader(ILogManager logManager, IHttpClient httpClient) + private readonly IServerConfigurationManager _config; + private readonly IEncryptionManager _encryption; + + public OpenSubtitleDownloader(ILogManager logManager, IHttpClient httpClient, IServerConfigurationManager config, IEncryptionManager encryption) { _logger = logManager.GetLogger(GetType().Name); _httpClient = httpClient; + _config = config; + _encryption = encryption; + + _config.ConfigurationUpdating += _config_ConfigurationUpdating; + } + + private const string PasswordHashPrefix = "h:"; + void _config_ConfigurationUpdating(object sender, GenericEventArgs e) + { + var options = e.Argument.SubtitleOptions; + + if (options != null && + !string.IsNullOrWhiteSpace(options.OpenSubtitlesPasswordHash) && + !options.OpenSubtitlesPasswordHash.StartsWith(PasswordHashPrefix, StringComparison.OrdinalIgnoreCase)) + { + options.OpenSubtitlesPasswordHash = EncryptPassword(options.OpenSubtitlesPasswordHash); + } + } + + private string EncryptPassword(string password) + { + return PasswordHashPrefix + _encryption.EncryptString(password); + } + + private string DecryptPassword(string password) + { + if (password == null || + !password.StartsWith(PasswordHashPrefix, StringComparison.OrdinalIgnoreCase)) + { + return string.Empty; + } + + return _encryption.DecryptString(password.Substring(2)); } public string Name @@ -35,7 +73,16 @@ namespace MediaBrowser.Providers.Subtitles public IEnumerable SupportedMediaTypes { - get { return new[] { SubtitleMediaType.Episode, SubtitleMediaType.Movie }; } + get + { + if (string.IsNullOrWhiteSpace(_config.Configuration.SubtitleOptions.OpenSubtitlesUsername) || + string.IsNullOrWhiteSpace(_config.Configuration.SubtitleOptions.OpenSubtitlesPasswordHash)) + { + return new SubtitleMediaType[] { }; + } + + return new[] { SubtitleMediaType.Episode, SubtitleMediaType.Movie }; + } } public Task GetSubtitles(string id, CancellationToken cancellationToken) @@ -59,7 +106,10 @@ namespace MediaBrowser.Providers.Subtitles var downloadsList = new[] { int.Parse(ossId, _usCulture) }; - var resultDownLoad = OpenSubtitles.DownloadSubtitles(downloadsList); + await Login(cancellationToken).ConfigureAwait(false); + + var resultDownLoad = await OpenSubtitles.DownloadSubtitlesAsync(downloadsList, cancellationToken).ConfigureAwait(false); + if (!(resultDownLoad is MethodResponseSubtitleDownload)) { throw new ApplicationException("Invalid response type"); @@ -77,6 +127,21 @@ namespace MediaBrowser.Providers.Subtitles }; } + private async Task Login(CancellationToken cancellationToken) + { + var options = _config.Configuration.SubtitleOptions ?? new SubtitleOptions(); + + var user = options.OpenSubtitlesUsername ?? string.Empty; + var password = DecryptPassword(options.OpenSubtitlesPasswordHash); + + var loginResponse = await OpenSubtitles.LogInAsync(user, password, "en", cancellationToken).ConfigureAwait(false); + + if (!(loginResponse is MethodResponseLogIn)) + { + throw new UnauthorizedAccessException("Authentication to OpenSubtitles failed."); + } + } + public async Task> SearchSubtitles(SubtitleSearchRequest request, CancellationToken cancellationToken) { var imdbIdText = request.GetProviderId(MetadataProviders.Imdb); @@ -116,13 +181,7 @@ namespace MediaBrowser.Providers.Subtitles Utilities.HttpClient = _httpClient; OpenSubtitles.SetUserAgent("OS Test User Agent"); - var loginResponse = await OpenSubtitles.LogInAsync("", "", "en", cancellationToken).ConfigureAwait(false); - - if (!(loginResponse is MethodResponseLogIn)) - { - _logger.Debug("Login error"); - return new List(); - } + await Login(cancellationToken).ConfigureAwait(false); var subLanguageId = request.Language; var hash = Utilities.ComputeHash(request.MediaPath); @@ -178,5 +237,10 @@ namespace MediaBrowser.Providers.Subtitles IsHashMatch = i.MovieHash == hasCopy }); } + + public void Dispose() + { + _config.ConfigurationUpdating -= _config_ConfigurationUpdating; + } } } diff --git a/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs b/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs index da38616821..ab79c6a1af 100644 --- a/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs +++ b/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs @@ -1,4 +1,5 @@ using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Events; using MediaBrowser.Common.Implementations.Configuration; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; @@ -29,6 +30,8 @@ namespace MediaBrowser.Server.Implementations.Configuration UpdateMetadataPath(); } + public event EventHandler> ConfigurationUpdating; + /// /// Gets the type of the configuration. /// @@ -73,8 +76,8 @@ namespace MediaBrowser.Server.Implementations.Configuration /// private void UpdateItemsByNamePath() { - ((ServerApplicationPaths) ApplicationPaths).ItemsByNamePath = string.IsNullOrEmpty(Configuration.ItemsByNamePath) ? - null : + ((ServerApplicationPaths)ApplicationPaths).ItemsByNamePath = string.IsNullOrEmpty(Configuration.ItemsByNamePath) ? + null : Configuration.ItemsByNamePath; } @@ -105,13 +108,15 @@ namespace MediaBrowser.Server.Implementations.Configuration /// public override void ReplaceConfiguration(BaseApplicationConfiguration newConfiguration) { - var newConfig = (ServerConfiguration) newConfiguration; + var newConfig = (ServerConfiguration)newConfiguration; ValidateItemByNamePath(newConfig); ValidateTranscodingTempPath(newConfig); ValidatePathSubstitutions(newConfig); ValidateMetadataPath(newConfig); + EventHelper.FireEventIfNotNull(ConfigurationUpdating, this, new GenericEventArgs { Argument = newConfig }, Logger); + base.ReplaceConfiguration(newConfiguration); } diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json index c464b2534b..52ddcd795f 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json @@ -17,8 +17,6 @@ "PasswordResetConfirmation": "Are you sure you wish to reset the password?", "PasswordSaved": "Password saved.", "PasswordMatchError": "Password and password confirmation must match.", - "OptionOff": "Off", - "OptionOn": "On", "OptionRelease": "Official Release", "OptionBeta": "Beta", "OptionDev": "Dev (Unstable)", diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index c2e29649fd..d011d45d06 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -147,10 +147,8 @@ "ScheduledTasksTitle": "Scheduled Tasks", "TabMyPlugins": "My Plugins", "TabCatalog": "Catalog", - "TabUpdates": "Updates", "PluginsTitle": "Plugins", "HeaderAutomaticUpdates": "Automatic Updates", - "HeaderUpdateLevel": "Update Level", "HeaderNowPlaying": "Now Playing", "HeaderLatestAlbums": "Latest Albums", "HeaderLatestSongs": "Latest Songs", @@ -706,5 +704,13 @@ "OptionEnableM2tsModeHelp": "Enable m2ts mode when encoding to mpegts.", "OptionEstimateContentLength": "Estimate content length when transcoding", "OptionReportByteRangeSeekingWhenTranscoding": "Report that the server supports byte seeking when transcoding", - "OptionReportByteRangeSeekingWhenTranscodingHelp": "This is required for some devices that don't time seek very well." + "OptionReportByteRangeSeekingWhenTranscodingHelp": "This is required for some devices that don't time seek very well.", + "HeaderSubtitleDownloadingHelp": "Media Browser can inspect your video files for missing subtitles, and download them using a subtitle provider such as OpenSubtitles.org.", + "HeaderDownloadSubtitlesFor": "Download subtitles for:", + "LabelRequireExternalSubtitles": "Download even if the video already contains graphical subtitles", + "LabelRequireExternalSubtitlesHelp": "Keeping text versions of subtitles will result in more efficient delivery to mobile clients.", + "TabSubtitles": "Subtitles", + "LabelOpenSubtitlesUsername": "Open Subtitles username:", + "LabelOpenSubtitlesPassword": "Open Subtitles password:", + "LabelAudioLanguagePreferenceHelp": "If empty, the default audio track will be selected, regardless of language." } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 3532ee3703..74b8bf2699 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -64,6 +64,7 @@ + @@ -201,6 +202,7 @@ + diff --git a/MediaBrowser.Server.Implementations/Security/EncryptionManager.cs b/MediaBrowser.Server.Implementations/Security/EncryptionManager.cs new file mode 100644 index 0000000000..73a4e30048 --- /dev/null +++ b/MediaBrowser.Server.Implementations/Security/EncryptionManager.cs @@ -0,0 +1,36 @@ +using MediaBrowser.Controller.Security; +using System; +using System.Security.Cryptography; +using System.Text; + +namespace MediaBrowser.Server.Implementations.Security +{ + public class EncryptionManager : IEncryptionManager + { + /// + /// Encrypts the string. + /// + /// The value. + /// System.String. + /// value + public string EncryptString(string value) + { + if (value == null) throw new ArgumentNullException("value"); + + return Encoding.Default.GetString(ProtectedData.Protect(Encoding.Default.GetBytes(value), null, DataProtectionScope.LocalMachine)); + } + + /// + /// Decrypts the string. + /// + /// The value. + /// System.String. + /// value + public string DecryptString(string value) + { + if (value == null) throw new ArgumentNullException("value"); + + return Encoding.Default.GetString(ProtectedData.Unprotect(Encoding.Default.GetBytes(value), null, DataProtectionScope.LocalMachine)); + } + } +} diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index c79d84e5a7..9de3198518 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -29,6 +29,7 @@ using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Resolvers; +using MediaBrowser.Controller.Security; using MediaBrowser.Controller.Session; using MediaBrowser.Controller.Sorting; using MediaBrowser.Controller.Subtitles; @@ -61,6 +62,7 @@ using MediaBrowser.Server.Implementations.Localization; using MediaBrowser.Server.Implementations.MediaEncoder; using MediaBrowser.Server.Implementations.Notifications; using MediaBrowser.Server.Implementations.Persistence; +using MediaBrowser.Server.Implementations.Security; using MediaBrowser.Server.Implementations.ServerManager; using MediaBrowser.Server.Implementations.Session; using MediaBrowser.Server.Implementations.Themes; @@ -533,6 +535,8 @@ namespace MediaBrowser.ServerApplication NotificationManager = new NotificationManager(LogManager, UserManager, ServerConfigurationManager); RegisterSingleInstance(NotificationManager); + RegisterSingleInstance(new EncryptionManager()); + SubtitleManager = new SubtitleManager(LogManager.GetLogger("SubtitleManager"), FileSystemManager, LibraryMonitor); RegisterSingleInstance(SubtitleManager); diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index 2db493f021..58a0c84b0f 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -585,6 +585,7 @@ namespace MediaBrowser.WebDashboard.Api "medialibrarypage.js", "metadataconfigurationpage.js", "metadataimagespage.js", + "metadatasubtitles.js", "moviegenres.js", "moviecollections.js", "movies.js", @@ -605,7 +606,6 @@ namespace MediaBrowser.WebDashboard.Api "playlist.js", "plugincatalogpage.js", "pluginspage.js", - "pluginupdatespage.js", "remotecontrol.js", "scheduledtaskpage.js", "scheduledtaskspage.js", diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index 3072413f92..7f9d6919e0 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -328,6 +328,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -658,6 +661,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -1781,16 +1787,6 @@ PreserveNewest - - - PreserveNewest - - - - - PreserveNewest - - PreserveNewest diff --git a/OpenSubtitlesHandler/OpenSubtitles.cs b/OpenSubtitlesHandler/OpenSubtitles.cs index 5353586c85..e810dad69c 100644 --- a/OpenSubtitlesHandler/OpenSubtitles.cs +++ b/OpenSubtitlesHandler/OpenSubtitles.cs @@ -551,6 +551,117 @@ namespace OpenSubtitlesHandler } return new MethodResponseError("Fail", "DownloadSubtitles call failed !"); } + + public static async Task DownloadSubtitlesAsync(int[] subIDS, CancellationToken cancellationToken) + { + if (TOKEN == "") + { + OSHConsole.WriteLine("Can't do this call, 'token' value not set. Please use Log In method first.", DebugCode.Error); + return new MethodResponseError("Fail", "Can't do this call, 'token' value not set. Please use Log In method first."); + } + if (subIDS == null) + { + OSHConsole.UpdateLine("No subtitle id passed !!", DebugCode.Error); + return new MethodResponseError("Fail", "No subtitle id passed"); ; + } + if (subIDS.Length == 0) + { + OSHConsole.UpdateLine("No subtitle id passed !!", DebugCode.Error); + return new MethodResponseError("Fail", "No subtitle id passed"); ; + } + // Method call .. + List parms = new List(); + // Add token param + parms.Add(new XmlRpcValueBasic(TOKEN, XmlRpcBasicValueType.String)); + // Add subtitle search parameters. Each one will be like 'array' of structs. + XmlRpcValueArray array = new XmlRpcValueArray(); + foreach (int id in subIDS) + { + array.Values.Add(new XmlRpcValueBasic(id, XmlRpcBasicValueType.Int)); + } + // Add the array to the parameters + parms.Add(array); + // Call ! + XmlRpcMethodCall call = new XmlRpcMethodCall("DownloadSubtitles", parms); + OSHConsole.WriteLine("Sending DownloadSubtitles request to the server ...", DebugCode.Good); + // Send the request to the server + + var httpResponse = await Utilities.SendRequestAsync(XmlRpcGenerator.Generate(call), XML_PRC_USERAGENT, cancellationToken).ConfigureAwait(false); + + string response = Utilities.GetStreamString(httpResponse); + if (!response.Contains("ERROR:")) + { + // No error occur, get and decode the response. + XmlRpcMethodCall[] calls = XmlRpcGenerator.DecodeMethodResponse(response); + if (calls.Length > 0) + { + if (calls[0].Parameters.Count > 0) + { + // We expect Struct of 3 members: + //* the first is status + //* the second is [array of structs, each one includes subtitle file]. + //* the third is [double basic value] represent seconds token by server. + XmlRpcValueStruct mainStruct = (XmlRpcValueStruct)calls[0].Parameters[0]; + // Create the response, we'll need it later + MethodResponseSubtitleDownload R = new MethodResponseSubtitleDownload(); + + // To make sure response is not currepted by server, do it in loop + foreach (XmlRpcStructMember MEMBER in mainStruct.Members) + { + if (MEMBER.Name == "status") + { + R.Status = (string)MEMBER.Data.Data; + OSHConsole.WriteLine("Status= " + R.Status); + } + else if (MEMBER.Name == "seconds") + { + R.Seconds = (double)MEMBER.Data.Data; + OSHConsole.WriteLine("Seconds= " + R.Seconds); + } + else if (MEMBER.Name == "data") + { + if (MEMBER.Data is XmlRpcValueArray) + { + OSHConsole.WriteLine("Download results:"); + XmlRpcValueArray rarray = (XmlRpcValueArray)MEMBER.Data; + foreach (IXmlRpcValue subStruct in rarray.Values) + { + if (subStruct == null) continue; + if (!(subStruct is XmlRpcValueStruct)) continue; + + SubtitleDownloadResult result = new SubtitleDownloadResult(); + foreach (XmlRpcStructMember submember in ((XmlRpcValueStruct)subStruct).Members) + { + // To avoid errors of arranged info or missing ones, let's do it with switch.. + switch (submember.Name) + { + case "idsubtitlefile": result.IdSubtitleFile = (string)submember.Data.Data; break; + case "data": result.Data = (string)submember.Data.Data; break; + } + } + R.Results.Add(result); + OSHConsole.WriteLine("> IDSubtilteFile= " + result.ToString()); + } + } + else// Unknown data ? + { + OSHConsole.WriteLine("Data= " + MEMBER.Data.Data.ToString(), DebugCode.Warning); + } + } + } + // Return the response to user !! + return R; + } + } + } + else + { + OSHConsole.WriteLine(response, DebugCode.Error); + return new MethodResponseError("Fail", response); + } + return new MethodResponseError("Fail", "DownloadSubtitles call failed !"); + } + /// /// Returns comments for subtitles /// -- cgit v1.2.3 From ad7af6355db70793b7302be8202962b4ab300076 Mon Sep 17 00:00:00 2001 From: Luis Miguel Almánzar Date: Thu, 8 May 2014 23:24:58 -0400 Subject: fix episodes subtitles --- .../Subtitles/OpenSubtitleDownloader.cs | 37 +++++++++------- .../SubtitleTypes/SubtitleSearchParameters.cs | 50 +--------------------- 2 files changed, 24 insertions(+), 63 deletions(-) (limited to 'OpenSubtitlesHandler') diff --git a/MediaBrowser.Providers/Subtitles/OpenSubtitleDownloader.cs b/MediaBrowser.Providers/Subtitles/OpenSubtitleDownloader.cs index 7b40b5673a..a8e9ff4a85 100644 --- a/MediaBrowser.Providers/Subtitles/OpenSubtitleDownloader.cs +++ b/MediaBrowser.Providers/Subtitles/OpenSubtitleDownloader.cs @@ -166,14 +166,7 @@ namespace MediaBrowser.Providers.Subtitles public async Task> SearchSubtitles(SubtitleSearchRequest request, CancellationToken cancellationToken) { var imdbIdText = request.GetProviderId(MetadataProviders.Imdb); - long imdbId; - - if (string.IsNullOrWhiteSpace(imdbIdText) || - !long.TryParse(imdbIdText.TrimStart('t'), NumberStyles.Any, _usCulture, out imdbId)) - { - _logger.Debug("Imdb id missing"); - return new List(); - } + long imdbId = 0; switch (request.ContentType) { @@ -190,6 +183,11 @@ namespace MediaBrowser.Providers.Subtitles _logger.Debug("Movie name missing"); return new List(); } + if (string.IsNullOrWhiteSpace(imdbIdText) || !long.TryParse(imdbIdText.TrimStart('t'), NumberStyles.Any, _usCulture, out imdbId)) + { + _logger.Debug("Imdb id missing"); + return new List(); + } break; } @@ -208,16 +206,25 @@ namespace MediaBrowser.Providers.Subtitles var hash = Utilities.ComputeHash(request.MediaPath); var fileInfo = new FileInfo(request.MediaPath); var movieByteSize = fileInfo.Length; - + var searchImdbId = request.ContentType == SubtitleMediaType.Movie ? imdbId.ToString(_usCulture) : ""; var subtitleSearchParameters = request.ContentType == SubtitleMediaType.Episode - ? new SubtitleSearchParameters(subLanguageId, request.SeriesName, request.ParentIndexNumber.Value.ToString(_usCulture), request.IndexNumber.Value.ToString(_usCulture)) - : new SubtitleSearchParameters(subLanguageId, request.Name); - + ? new List { + new SubtitleSearchParameters(subLanguageId, + query: request.SeriesName, + season: request.ParentIndexNumber.Value.ToString(_usCulture), + episode: request.IndexNumber.Value.ToString(_usCulture)) + } + : new List { + new SubtitleSearchParameters(subLanguageId, imdbid: searchImdbId), + new SubtitleSearchParameters(subLanguageId, query: request.Name, imdbid: searchImdbId) + }; var parms = new List { - new SubtitleSearchParameters(subLanguageId, hash, movieByteSize), - subtitleSearchParameters + new SubtitleSearchParameters( subLanguageId, + movieHash: hash, + movieByteSize: movieByteSize, + imdbid: searchImdbId ), }; - + parms.AddRange(subtitleSearchParameters); var result = OpenSubtitles.SearchSubtitles(parms.ToArray()); if (!(result is MethodResponseSubtitleSearch)) { diff --git a/OpenSubtitlesHandler/SubtitleTypes/SubtitleSearchParameters.cs b/OpenSubtitlesHandler/SubtitleTypes/SubtitleSearchParameters.cs index f2e70ef813..46af6215ba 100644 --- a/OpenSubtitlesHandler/SubtitleTypes/SubtitleSearchParameters.cs +++ b/OpenSubtitlesHandler/SubtitleTypes/SubtitleSearchParameters.cs @@ -25,63 +25,17 @@ namespace OpenSubtitlesHandler /// public struct SubtitleSearchParameters { - /// - /// Paramaters for subtitle search call - /// - /// List of language ISO639-3 language codes to search for, divided by ',' (e.g. 'cze,eng,slo') - /// Video file hash as calculated by one of the implementation functions as seen on http://trac.opensubtitles.org/projects/opensubtitles/wiki/HashSourceCodes - /// Size of video file in bytes - public SubtitleSearchParameters(string subLanguageId, string movieHash, long movieByteSize) + public SubtitleSearchParameters(string subLanguageId, string query = "", string season = "", string episode = "", string movieHash = "", long movieByteSize = 0, string imdbid = "") { this.subLanguageId = subLanguageId; this.movieHash = movieHash; this.movieByteSize = movieByteSize; - this.imdbid = ""; - this._episode = ""; - this._season = ""; - this._query = ""; - } - - public SubtitleSearchParameters(string subLanguageId, string query) - { - this.subLanguageId = subLanguageId; - this.movieHash = ""; - this.movieByteSize = 0; - this.imdbid = ""; - this._episode = ""; - this._season = ""; - this._query = query; - } - - public SubtitleSearchParameters(string subLanguageId, string query, string season, string episode) - { - this.subLanguageId = subLanguageId; - this.movieHash = ""; - this.movieByteSize = 0; - this.imdbid = ""; + this.imdbid = imdbid; this._episode = episode; this._season = season; this._query = query; } - /// - /// Paramaters for subtitle search call - /// - /// List of language ISO639-3 language codes to search for, divided by ',' (e.g. 'cze,eng,slo') - /// Video file hash as calculated by one of the implementation functions as seen on http://trac.opensubtitles.org/projects/opensubtitles/wiki/HashSourceCodes - /// Size of video file in bytes - /// IMDb ID of movie this video is part of, belongs to. - public SubtitleSearchParameters(string subLanguageId, string movieHash, long movieByteSize, string imdbid) - { - this.subLanguageId = subLanguageId; - this.movieHash = movieHash; - this.movieByteSize = movieByteSize; - this.imdbid = imdbid; - this._episode = ""; - this._season = ""; - this._query = ""; - } - private string subLanguageId; private string movieHash; private long movieByteSize; -- cgit v1.2.3 From 1a323767be3808f8cdd055e8481ca8c1ea0b1582 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 9 May 2014 00:38:12 -0400 Subject: Do better to make sure hls files are cleaned up --- MediaBrowser.Api/ApiEntryPoint.cs | 36 +--- .../DefaultTheme/DefaultThemeService.cs | 13 +- MediaBrowser.Api/Playback/BaseStreamingService.cs | 2 +- MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs | 4 +- MediaBrowser.Api/Playback/Hls/VideoHlsService.cs | 4 +- .../Playback/Progressive/VideoService.cs | 4 +- MediaBrowser.Controller/Entities/TV/Episode.cs | 8 +- .../Configuration/MetadataOptions.cs | 20 ++- .../Configuration/NotificationOptions.cs | 6 +- .../Configuration/NotificationType.cs | 1 + MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs | 31 ++-- MediaBrowser.Model/Dlna/Filter.cs | 7 +- MediaBrowser.Model/Dlna/TranscodingProfile.cs | 1 - MediaBrowser.Model/Entities/MediaStream.cs | 18 +- MediaBrowser.Model/Web/QueryStringDictionary.cs | 11 +- .../EntryPoints/Notifications/Notifications.cs | 21 ++- .../Localization/Server/server.json | 7 +- .../Notifications/CoreNotificationTypes.cs | 7 + OpenSubtitlesHandler/OpenSubtitles.cs | 181 +++++++++++++++++++++ 19 files changed, 297 insertions(+), 85 deletions(-) (limited to 'OpenSubtitlesHandler') diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index f35ade4324..9b04ec0117 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/MediaBrowser.Api/ApiEntryPoint.cs @@ -71,8 +71,7 @@ namespace MediaBrowser.Api /// private void DeleteEncodedMediaCache() { - foreach (var file in Directory.EnumerateFiles(_appPaths.TranscodingTempPath) - .Where(i => EntityResolutionHelper.VideoFileExtensions.Contains(Path.GetExtension(i))) + foreach (var file in Directory.EnumerateFiles(_appPaths.TranscodingTempPath, "*", SearchOption.AllDirectories) .ToList()) { File.Delete(file); @@ -116,11 +115,10 @@ namespace MediaBrowser.Api /// The path. /// The type. /// The process. - /// if set to true [is video]. /// The start time ticks. /// The source path. /// The device id. - public void OnTranscodeBeginning(string path, TranscodingJobType type, Process process, bool isVideo, long? startTimeTicks, string sourcePath, string deviceId) + public void OnTranscodeBeginning(string path, TranscodingJobType type, Process process, long? startTimeTicks, string sourcePath, string deviceId) { lock (_activeTranscodingJobs) { @@ -130,7 +128,6 @@ namespace MediaBrowser.Api Path = path, Process = process, ActiveRequestCount = 1, - IsVideo = isVideo, StartTimeTicks = startTimeTicks, SourcePath = sourcePath, DeviceId = deviceId @@ -261,7 +258,7 @@ namespace MediaBrowser.Api { // This is really only needed for HLS. // Progressive streams can stop on their own reliably - jobs.AddRange(_activeTranscodingJobs.Where(i => isVideo == i.IsVideo && string.Equals(deviceId, i.DeviceId, StringComparison.OrdinalIgnoreCase))); + jobs.AddRange(_activeTranscodingJobs.Where(i => string.Equals(deviceId, i.DeviceId, StringComparison.OrdinalIgnoreCase))); } foreach (var job in jobs) @@ -325,37 +322,15 @@ namespace MediaBrowser.Api } } - // Determine if it exited successfully - var hasExitedSuccessfully = false; - - try - { - hasExitedSuccessfully = process.ExitCode == 0; - } - catch (InvalidOperationException) - { - - } - catch (NotSupportedException) - { - - } - // Dispose the process process.Dispose(); - // If it didn't complete successfully cleanup the partial files - // Also don't cache output from resume points - // Also don't cache video - if (!hasExitedSuccessfully || job.StartTimeTicks.HasValue || job.IsVideo) - { - DeletePartialStreamFiles(job.Path, job.Type, 0, 1500); - } + DeletePartialStreamFiles(job.Path, job.Type, 0, 1500); } private async void DeletePartialStreamFiles(string path, TranscodingJobType jobType, int retryCount, int delayMs) { - if (retryCount >= 5) + if (retryCount >= 10) { return; } @@ -455,7 +430,6 @@ namespace MediaBrowser.Api /// The kill timer. public Timer KillTimer { get; set; } - public bool IsVideo { get; set; } public long? StartTimeTicks { get; set; } public string SourcePath { get; set; } public string DeviceId { get; set; } diff --git a/MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs b/MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs index cd04a82121..6acecd342d 100644 --- a/MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs +++ b/MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs @@ -24,6 +24,8 @@ namespace MediaBrowser.Api.DefaultTheme [ApiMember(Name = "RecentlyPlayedGamesLimit", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public int RecentlyPlayedGamesLimit { get; set; } + + public string ParentId { get; set; } } [Route("/MBT/DefaultTheme/TV", "GET")] @@ -49,6 +51,8 @@ namespace MediaBrowser.Api.DefaultTheme [ApiMember(Name = "LatestEpisodeLimit", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public int LatestEpisodeLimit { get; set; } + + public string ParentId { get; set; } } [Route("/MBT/DefaultTheme/Movies", "GET")] @@ -71,6 +75,8 @@ namespace MediaBrowser.Api.DefaultTheme [ApiMember(Name = "LatestTrailersLimit", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public int LatestTrailersLimit { get; set; } + + public string ParentId { get; set; } } [Route("/MBT/DefaultTheme/Favorites", "GET")] @@ -224,7 +230,7 @@ namespace MediaBrowser.Api.DefaultTheme { var user = _userManager.GetUserById(request.UserId); - var items = user.RootFolder.GetRecursiveChildren(user, i => i is Game || i is GameSystem) + var items = GetAllLibraryItems(user.Id, _userManager, _libraryManager, request.ParentId).Where(i => i is Game || i is GameSystem) .ToList(); var gamesWithImages = items.OfType().Where(i => !string.IsNullOrEmpty(i.PrimaryImagePath)).ToList(); @@ -280,7 +286,7 @@ namespace MediaBrowser.Api.DefaultTheme var user = _userManager.GetUserById(request.UserId); - var series = user.RootFolder.GetRecursiveChildren(user) + var series = GetAllLibraryItems(user.Id, _userManager, _libraryManager, request.ParentId) .OfType() .ToList(); @@ -403,7 +409,8 @@ namespace MediaBrowser.Api.DefaultTheme { var user = _userManager.GetUserById(request.UserId); - var items = user.RootFolder.GetRecursiveChildren(user, i => i is Movie || i is Trailer || i is BoxSet) + var items = GetAllLibraryItems(user.Id, _userManager, _libraryManager, request.ParentId) + .Where(i => i is Movie || i is Trailer || i is BoxSet) .ToList(); var view = new MoviesView(); diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 7e352e4de1..23209b59ce 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -903,7 +903,7 @@ namespace MediaBrowser.Api.Playback EnableRaisingEvents = true }; - ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath, TranscodingJobType, process, state.IsInputVideo, state.Request.StartTimeTicks, state.MediaPath, state.Request.DeviceId); + ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath, TranscodingJobType, process, state.Request.StartTimeTicks, state.MediaPath, state.Request.DeviceId); var commandLineLogMessage = process.StartInfo.FileName + " " + process.StartInfo.Arguments; Logger.Info(commandLineLogMessage); diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index c865504045..fd93ef6852 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -273,9 +273,7 @@ namespace MediaBrowser.Api.Playback.Hls const string keyFrameArg = " -force_key_frames expr:if(isnan(prev_forced_t),gte(t,.1),gte(t,prev_forced_t+5))"; - var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsExternal && - (state.SubtitleStream.Codec.IndexOf("pgs", StringComparison.OrdinalIgnoreCase) != -1 || - state.SubtitleStream.Codec.IndexOf("dvd", StringComparison.OrdinalIgnoreCase) != -1); + var hasGraphicalSubs = state.SubtitleStream != null && state.SubtitleStream.IsGraphicalSubtitleStream; var args = "-codec:v:0 " + codec + " " + GetVideoQualityParam(state, "libx264", true) + keyFrameArg; diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs index efd98616f9..77ac95815d 100644 --- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs @@ -168,9 +168,7 @@ namespace MediaBrowser.Api.Playback.Hls " -force_key_frames expr:if(isnan(prev_forced_t),gte(t,.1),gte(t,prev_forced_t+1))" : " -force_key_frames expr:if(isnan(prev_forced_t),gte(t,.1),gte(t,prev_forced_t+5))"; - var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsExternal && - (state.SubtitleStream.Codec.IndexOf("pgs", StringComparison.OrdinalIgnoreCase) != -1 || - state.SubtitleStream.Codec.IndexOf("dvd", StringComparison.OrdinalIgnoreCase) != -1); + var hasGraphicalSubs = state.SubtitleStream != null && state.SubtitleStream.IsGraphicalSubtitleStream; var args = "-codec:v:0 " + codec + " " + GetVideoQualityParam(state, "libx264", true) + keyFrameArg; diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs index f21e69290e..d7061ae754 100644 --- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs +++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs @@ -146,9 +146,7 @@ namespace MediaBrowser.Api.Playback.Progressive args += keyFrameArg; - var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsExternal && - (state.SubtitleStream.Codec.IndexOf("pgs", StringComparison.OrdinalIgnoreCase) != -1 || - state.SubtitleStream.Codec.IndexOf("dvd", StringComparison.OrdinalIgnoreCase) != -1); + var hasGraphicalSubs = state.SubtitleStream != null && state.SubtitleStream.IsGraphicalSubtitleStream; var request = state.VideoRequest; diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs index 503d1513c1..be761ef662 100644 --- a/MediaBrowser.Controller/Entities/TV/Episode.cs +++ b/MediaBrowser.Controller/Entities/TV/Episode.cs @@ -305,13 +305,7 @@ namespace MediaBrowser.Controller.Entities.TV if (!ParentIndexNumber.HasValue && !string.IsNullOrEmpty(Path)) { - ParentIndexNumber = TVUtils.GetSeasonNumberFromPath(Path); - - // If a change was made record it - if (ParentIndexNumber.HasValue) - { - hasChanges = true; - } + ParentIndexNumber = TVUtils.GetSeasonNumberFromEpisodeFile(Path); } // If a change was made record it diff --git a/MediaBrowser.Model/Configuration/MetadataOptions.cs b/MediaBrowser.Model/Configuration/MetadataOptions.cs index 88fa486f97..fdfbbf4f42 100644 --- a/MediaBrowser.Model/Configuration/MetadataOptions.cs +++ b/MediaBrowser.Model/Configuration/MetadataOptions.cs @@ -52,14 +52,30 @@ namespace MediaBrowser.Model.Configuration public int GetLimit(ImageType type) { - ImageOption option = ImageOptions.FirstOrDefault(i => i.Type == type); + ImageOption option = null; + foreach (ImageOption i in ImageOptions) + { + if (i.Type == type) + { + option = i; + break; + } + } return option == null ? 1 : option.Limit; } public int GetMinWidth(ImageType type) { - ImageOption option = ImageOptions.FirstOrDefault(i => i.Type == type); + ImageOption option = null; + foreach (ImageOption i in ImageOptions) + { + if (i.Type == type) + { + option = i; + break; + } + } return option == null ? 0 : option.MinWidth; } diff --git a/MediaBrowser.Model/Configuration/NotificationOptions.cs b/MediaBrowser.Model/Configuration/NotificationOptions.cs index 0ed43ae1eb..d6517e895d 100644 --- a/MediaBrowser.Model/Configuration/NotificationOptions.cs +++ b/MediaBrowser.Model/Configuration/NotificationOptions.cs @@ -70,7 +70,11 @@ namespace MediaBrowser.Model.Configuration public NotificationOption GetOptions(string type) { - return Options.FirstOrDefault(i => string.Equals(type, i.Type, StringComparison.OrdinalIgnoreCase)); + foreach (NotificationOption i in Options) + { + if (string.Equals(type, i.Type, StringComparison.OrdinalIgnoreCase)) return i; + } + return null; } public bool IsEnabled(string type) diff --git a/MediaBrowser.Model/Configuration/NotificationType.cs b/MediaBrowser.Model/Configuration/NotificationType.cs index eaafb651ce..0ddcf42517 100644 --- a/MediaBrowser.Model/Configuration/NotificationType.cs +++ b/MediaBrowser.Model/Configuration/NotificationType.cs @@ -12,6 +12,7 @@ namespace MediaBrowser.Model.Configuration PluginUpdateInstalled, PluginUninstalled, NewLibraryContent, + NewLibraryContentMultiple, ServerRestartRequired, TaskFailed, VideoPlayback diff --git a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs index 5309df1310..a7ad49cba2 100644 --- a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs +++ b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs @@ -1,6 +1,5 @@ using MediaBrowser.Model.MediaInfo; using System.Collections.Generic; -using System.Linq; namespace MediaBrowser.Model.Dlna { @@ -158,11 +157,21 @@ namespace MediaBrowser.Model.Dlna if (string.IsNullOrEmpty(orgPn)) { - orgPn = GetVideoOrgPnValue(container, videoCodec, audioCodec, width, height, timestamp) - .FirstOrDefault(); + foreach (string s in GetVideoOrgPnValue(container, videoCodec, audioCodec, width, height, timestamp)) + { + orgPn = s; + break; + } + } + if (string.IsNullOrEmpty(orgPn)) + { // TODO: Support multiple values and return multiple headers? - orgPn = (orgPn ?? string.Empty).Split(',').FirstOrDefault(); + foreach (string s in (orgPn ?? string.Empty).Split(',')) + { + orgPn = s; + break; + } } string contentFeatures = string.IsNullOrEmpty(orgPn) ? string.Empty : "DLNA.ORG_PN=" + orgPn; @@ -191,16 +200,12 @@ namespace MediaBrowser.Model.Dlna return format.HasValue ? format.Value.ToString() : null; } - private IEnumerable GetVideoOrgPnValue(string container, string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestamp) + private List GetVideoOrgPnValue(string container, string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestamp) { - return new MediaFormatProfileResolver() - .ResolveVideoFormat(container, - videoCodec, - audioCodec, - width, - height, - timestamp) - .Select(i => i.ToString()); + List list = new List(); + foreach (MediaFormatProfile i in new MediaFormatProfileResolver().ResolveVideoFormat(container, videoCodec, audioCodec, width, height, timestamp)) + list.Add(i.ToString()); + return list; } } } diff --git a/MediaBrowser.Model/Dlna/Filter.cs b/MediaBrowser.Model/Dlna/Filter.cs index c8940734b2..760adb5859 100644 --- a/MediaBrowser.Model/Dlna/Filter.cs +++ b/MediaBrowser.Model/Dlna/Filter.cs @@ -19,9 +19,10 @@ namespace MediaBrowser.Model.Dlna { _all = string.Equals(filter, "*", StringComparison.OrdinalIgnoreCase); - _fields = (filter ?? string.Empty) - .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) - .ToList(); + List list = new List(); + foreach (string s in (filter ?? string.Empty).Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries)) + list.Add(s); + _fields = list; } public bool Contains(string field) diff --git a/MediaBrowser.Model/Dlna/TranscodingProfile.cs b/MediaBrowser.Model/Dlna/TranscodingProfile.cs index bee5f2a691..51f4bfe619 100644 --- a/MediaBrowser.Model/Dlna/TranscodingProfile.cs +++ b/MediaBrowser.Model/Dlna/TranscodingProfile.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Linq; using System.Xml.Serialization; namespace MediaBrowser.Model.Dlna diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index 66163c1ef7..9f64b36e43 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -1,4 +1,6 @@ -using System.Diagnostics; +using System; +using System.Diagnostics; +using System.Runtime.Serialization; namespace MediaBrowser.Model.Entities { @@ -128,6 +130,20 @@ namespace MediaBrowser.Model.Entities /// true if this instance is external; otherwise, false. public bool IsExternal { get; set; } + [IgnoreDataMember] + public bool IsGraphicalSubtitleStream + { + get + { + if (IsExternal) return false; + + var codec = Codec ?? string.Empty; + + return codec.IndexOf("pgs", StringComparison.OrdinalIgnoreCase) != -1 || + codec.IndexOf("dvd", StringComparison.OrdinalIgnoreCase) != -1; + } + } + /// /// Gets or sets the filename. /// diff --git a/MediaBrowser.Model/Web/QueryStringDictionary.cs b/MediaBrowser.Model/Web/QueryStringDictionary.cs index b532358ff1..b011d4d9cb 100644 --- a/MediaBrowser.Model/Web/QueryStringDictionary.cs +++ b/MediaBrowser.Model/Web/QueryStringDictionary.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; namespace MediaBrowser.Model.Web @@ -24,7 +25,7 @@ namespace MediaBrowser.Model.Web /// The value. public void Add(string name, int value) { - Add(name, value.ToString()); + Add(name, value.ToString(CultureInfo.InvariantCulture)); } /// @@ -34,7 +35,7 @@ namespace MediaBrowser.Model.Web /// The value. public void Add(string name, long value) { - Add(name, value.ToString()); + Add(name, value.ToString(CultureInfo.InvariantCulture)); } /// @@ -44,7 +45,7 @@ namespace MediaBrowser.Model.Web /// The value. public void Add(string name, double value) { - Add(name, value.ToString()); + Add(name, value.ToString(CultureInfo.InvariantCulture)); } /// @@ -135,7 +136,7 @@ namespace MediaBrowser.Model.Web throw new ArgumentNullException("value"); } - Add(name, string.Join(",", value.Select(v => v.ToString()).ToArray())); + Add(name, string.Join(",", value.Select(v => v.ToString(CultureInfo.InvariantCulture)).ToArray())); } /// @@ -188,7 +189,7 @@ namespace MediaBrowser.Model.Web /// The name. /// The value. /// The delimiter. - /// value + /// value public void Add(string name, IEnumerable value, string delimiter) { if (value == null) diff --git a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs index 7607ec98a3..fdc81db373 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs +++ b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifications.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.Events; +using System.Globalization; +using MediaBrowser.Common.Events; using MediaBrowser.Common.Plugins; using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Common.Updates; @@ -247,10 +248,10 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications DisposeLibraryUpdateTimer(); } - var item = items.FirstOrDefault(); - - if (item != null) + if (items.Count == 1) { + var item = items.First(); + var notification = new NotificationRequest { NotificationType = NotificationType.NewLibraryContent.ToString() @@ -258,10 +259,16 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications notification.Variables["Name"] = item.Name; - if (items.Count > 1) + await SendNotification(notification).ConfigureAwait(false); + } + else + { + var notification = new NotificationRequest { - notification.Name = items.Count + " new library items."; - } + NotificationType = NotificationType.NewLibraryContentMultiple.ToString() + }; + + notification.Variables["ItemCount"] = items.Count.ToString(CultureInfo.InvariantCulture); await SendNotification(notification).ConfigureAwait(false); } diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index 781a4c5b70..bcd04f4ae3 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -568,6 +568,7 @@ "NotificationOptionTaskFailed": "Scheduled task failure", "NotificationOptionInstallationFailed": "Installation failure", "NotificationOptionNewLibraryContent": "New content added", + "NotificationOptionNewLibraryContentMultiple": "New content added (multiple)", "SendNotificationHelp": "By default, notifications are delivered to the dashboard inbox. Browse the plugin catalog to install additional notification options.", "NotificationOptionServerRestartRequired": "Server restart required", "LabelNotificationEnabled": "Enable this notification", @@ -716,5 +717,9 @@ "LabelDownloadLanguages": "Download languages:", "ButtonRegister": "Register", "LabelSkipIfAudioTrackPresent": "Skip if the default audio track matches the download language", - "LabelSkipIfAudioTrackPresentHelp": "Uncheck this to ensure all videos have subtitles, regardless of audio language." + "LabelSkipIfAudioTrackPresentHelp": "Uncheck this to ensure all videos have subtitles, regardless of audio language.", + "HeaderSendMessage": "Send Message", + "ButtonSend": "Send", + "LabelMessageText": "Message text:", + "LabelMessageTitle": "Message title:" } \ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Notifications/CoreNotificationTypes.cs b/MediaBrowser.Server.Implementations/Notifications/CoreNotificationTypes.cs index f712949d91..012b5ae927 100644 --- a/MediaBrowser.Server.Implementations/Notifications/CoreNotificationTypes.cs +++ b/MediaBrowser.Server.Implementations/Notifications/CoreNotificationTypes.cs @@ -90,6 +90,13 @@ namespace MediaBrowser.Server.Implementations.Notifications Variables = new List{"Name"} }, + new NotificationTypeInfo + { + Type = NotificationType.NewLibraryContentMultiple.ToString(), + DefaultTitle = "{ItemCount} new items have been added to your media library.", + Variables = new List{"ItemCount"} + }, + new NotificationTypeInfo { Type = NotificationType.AudioPlayback.ToString(), diff --git a/OpenSubtitlesHandler/OpenSubtitles.cs b/OpenSubtitlesHandler/OpenSubtitles.cs index e810dad69c..9452c25ece 100644 --- a/OpenSubtitlesHandler/OpenSubtitles.cs +++ b/OpenSubtitlesHandler/OpenSubtitles.cs @@ -440,6 +440,187 @@ namespace OpenSubtitlesHandler } return new MethodResponseError("Fail", "Search Subtitles call failed !"); } + + public static async Task SearchSubtitlesAsync(SubtitleSearchParameters[] parameters, CancellationToken cancellationToken) + { + if (TOKEN == "") + { + OSHConsole.WriteLine("Can't do this call, 'token' value not set. Please use Log In method first.", DebugCode.Error); + return new MethodResponseError("Fail", "Can't do this call, 'token' value not set. Please use Log In method first."); + } + if (parameters == null) + { + OSHConsole.UpdateLine("No subtitle search parameter passed !!", DebugCode.Error); + return new MethodResponseError("Fail", "No subtitle search parameter passed"); ; + } + if (parameters.Length == 0) + { + OSHConsole.UpdateLine("No subtitle search parameter passed !!", DebugCode.Error); + return new MethodResponseError("Fail", "No subtitle search parameter passed"); ; + } + // Method call .. + List parms = new List(); + // Add token param + parms.Add(new XmlRpcValueBasic(TOKEN, XmlRpcBasicValueType.String)); + // Add subtitle search parameters. Each one will be like 'array' of structs. + XmlRpcValueArray array = new XmlRpcValueArray(); + foreach (SubtitleSearchParameters param in parameters) + { + XmlRpcValueStruct strct = new XmlRpcValueStruct(new List()); + // sublanguageid member + XmlRpcStructMember member = new XmlRpcStructMember("sublanguageid", + new XmlRpcValueBasic(param.SubLangaugeID, XmlRpcBasicValueType.String)); + strct.Members.Add(member); + // moviehash member + if (param.MovieHash.Length > 0 && param.MovieByteSize > 0) + { + member = new XmlRpcStructMember("moviehash", + new XmlRpcValueBasic(param.MovieHash, XmlRpcBasicValueType.String)); + strct.Members.Add(member); + // moviehash member + member = new XmlRpcStructMember("moviebytesize", + new XmlRpcValueBasic(param.MovieByteSize, XmlRpcBasicValueType.Int)); + strct.Members.Add(member); + } + if (param.Query.Length > 0) + { + member = new XmlRpcStructMember("query", + new XmlRpcValueBasic(param.Query, XmlRpcBasicValueType.String)); + strct.Members.Add(member); + } + + if (param.Episode.Length > 0 && param.Season.Length > 0) + { + member = new XmlRpcStructMember("season", + new XmlRpcValueBasic(param.Season, XmlRpcBasicValueType.String)); + strct.Members.Add(member); + member = new XmlRpcStructMember("episode", + new XmlRpcValueBasic(param.Episode, XmlRpcBasicValueType.String)); + strct.Members.Add(member); + } + + // imdbid member + if (param.IMDbID.Length > 0) + { + member = new XmlRpcStructMember("imdbid", + new XmlRpcValueBasic(param.IMDbID, XmlRpcBasicValueType.String)); + strct.Members.Add(member); + } + // Add the struct to the array + array.Values.Add(strct); + } + // Add the array to the parameters + parms.Add(array); + // Call ! + XmlRpcMethodCall call = new XmlRpcMethodCall("SearchSubtitles", parms); + OSHConsole.WriteLine("Sending SearchSubtitles request to the server ...", DebugCode.Good); + // Send the request to the server + string response = Utilities.GetStreamString(await Utilities.SendRequestAsync(XmlRpcGenerator.Generate(call), XML_PRC_USERAGENT, cancellationToken).ConfigureAwait(false)); + + if (!response.Contains("ERROR:")) + { + // No error occur, get and decode the response. + XmlRpcMethodCall[] calls = XmlRpcGenerator.DecodeMethodResponse(response); + if (calls.Length > 0) + { + if (calls[0].Parameters.Count > 0) + { + // We expect Struct of 3 members: + //* the first is status + //* the second is [array of structs, each one includes subtitle file]. + //* the third is [double basic value] represent seconds token by server. + XmlRpcValueStruct mainStruct = (XmlRpcValueStruct)calls[0].Parameters[0]; + // Create the response, we'll need it later + MethodResponseSubtitleSearch R = new MethodResponseSubtitleSearch(); + // To make sure response is not currepted by server, do it in loop + foreach (XmlRpcStructMember MEMBER in mainStruct.Members) + { + if (MEMBER.Name == "status") + { + R.Status = (string)MEMBER.Data.Data; + OSHConsole.WriteLine("Status= " + R.Status); + } + else if (MEMBER.Name == "seconds") + { + R.Seconds = (double)MEMBER.Data.Data; + OSHConsole.WriteLine("Seconds= " + R.Seconds); + } + else if (MEMBER.Name == "data") + { + if (MEMBER.Data is XmlRpcValueArray) + { + OSHConsole.WriteLine("Search results: "); + + XmlRpcValueArray rarray = (XmlRpcValueArray)MEMBER.Data; + foreach (IXmlRpcValue subStruct in rarray.Values) + { + if (subStruct == null) continue; + if (!(subStruct is XmlRpcValueStruct)) continue; + + SubtitleSearchResult result = new SubtitleSearchResult(); + foreach (XmlRpcStructMember submember in ((XmlRpcValueStruct)subStruct).Members) + { + // To avoid errors of arranged info or missing ones, let's do it with switch.. + switch (submember.Name) + { + case "IDMovie": result.IDMovie = submember.Data.Data.ToString(); break; + case "IDMovieImdb": result.IDMovieImdb = submember.Data.Data.ToString(); break; + case "IDSubMovieFile": result.IDSubMovieFile = submember.Data.Data.ToString(); break; + case "IDSubtitle": result.IDSubtitle = submember.Data.Data.ToString(); break; + case "IDSubtitleFile": result.IDSubtitleFile = submember.Data.Data.ToString(); break; + case "ISO639": result.ISO639 = submember.Data.Data.ToString(); break; + case "LanguageName": result.LanguageName = submember.Data.Data.ToString(); break; + case "MovieByteSize": result.MovieByteSize = submember.Data.Data.ToString(); break; + case "MovieHash": result.MovieHash = submember.Data.Data.ToString(); break; + case "MovieImdbRating": result.MovieImdbRating = submember.Data.Data.ToString(); break; + case "MovieName": result.MovieName = submember.Data.Data.ToString(); break; + case "MovieNameEng": result.MovieNameEng = submember.Data.Data.ToString(); break; + case "MovieReleaseName": result.MovieReleaseName = submember.Data.Data.ToString(); break; + case "MovieTimeMS": result.MovieTimeMS = submember.Data.Data.ToString(); break; + case "MovieYear": result.MovieYear = submember.Data.Data.ToString(); break; + case "SubActualCD": result.SubActualCD = submember.Data.Data.ToString(); break; + case "SubAddDate": result.SubAddDate = submember.Data.Data.ToString(); break; + case "SubAuthorComment": result.SubAuthorComment = submember.Data.Data.ToString(); break; + case "SubBad": result.SubBad = submember.Data.Data.ToString(); break; + case "SubDownloadLink": result.SubDownloadLink = submember.Data.Data.ToString(); break; + case "SubDownloadsCnt": result.SubDownloadsCnt = submember.Data.Data.ToString(); break; + case "SeriesEpisode": result.SeriesEpisode = submember.Data.Data.ToString(); break; + case "SeriesSeason": result.SeriesSeason = submember.Data.Data.ToString(); break; + case "SubFileName": result.SubFileName = submember.Data.Data.ToString(); break; + case "SubFormat": result.SubFormat = submember.Data.Data.ToString(); break; + case "SubHash": result.SubHash = submember.Data.Data.ToString(); break; + case "SubLanguageID": result.SubLanguageID = submember.Data.Data.ToString(); break; + case "SubRating": result.SubRating = submember.Data.Data.ToString(); break; + case "SubSize": result.SubSize = submember.Data.Data.ToString(); break; + case "SubSumCD": result.SubSumCD = submember.Data.Data.ToString(); break; + case "UserID": result.UserID = submember.Data.Data.ToString(); break; + case "UserNickName": result.UserNickName = submember.Data.Data.ToString(); break; + case "ZipDownloadLink": result.ZipDownloadLink = submember.Data.Data.ToString(); break; + } + } + R.Results.Add(result); + OSHConsole.WriteLine(">" + result.ToString()); + } + } + else// Unknown data ? + { + OSHConsole.WriteLine("Data= " + MEMBER.Data.Data.ToString(), DebugCode.Warning); + } + } + } + // Return the response to user !! + return R; + } + } + } + else + { + OSHConsole.WriteLine(response, DebugCode.Error); + return new MethodResponseError("Fail", response); + } + return new MethodResponseError("Fail", "Search Subtitles call failed !"); + } + /// /// Download subtitle file(s) /// -- cgit v1.2.3 From 4e816552395c1b57868ccd29f6f2e9b70d7272a5 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 10 May 2014 13:28:03 -0400 Subject: updated mono build --- MediaBrowser.Api/DisplayPreferencesService.cs | 11 +-- .../ScheduledTasksWebSocketListener.cs | 33 +++++++- .../WebSocket/LogFileWebSocketListener.cs | 2 +- .../WebSocket/SessionInfoWebSocketListener.cs | 4 +- .../WebSocket/SystemInfoWebSocketListener.cs | 4 +- .../ScheduledTasks/ScheduledTaskWorker.cs | 18 +++- .../ScheduledTasks/TaskManager.cs | 29 +++++-- .../Net/BasePeriodicWebSocketListener.cs | 98 ++++++++++++++++++---- .../ScheduledTasks/IScheduledTaskWorker.cs | 8 +- MediaBrowser.Common/ScheduledTasks/ITaskManager.cs | 5 +- .../ScheduledTasks/TaskCompletionEventArgs.cs | 2 +- .../MediaBrowser.MediaEncoding.csproj | 8 +- MediaBrowser.Mono.sln | 14 +++- MediaBrowser.Mono.userprefs | 2 +- .../EntryPoints/Notifications/Notifications.cs | 36 ++++++-- .../EntryPoints/ServerEventNotifier.cs | 4 +- .../SqliteDisplayPreferencesRepository.cs | 9 +- .../Session/SessionManager.cs | 20 +++-- MediaBrowser.Server.Mono/Native/NativeApp.cs | 5 ++ .../EntryPoints/KeepServerAwake.cs | 25 +----- .../FFMpeg/FFMpegDownloadInfo.cs | 8 +- MediaBrowser.ServerApplication/Native/NativeApp.cs | 29 ++++++- OpenSubtitlesHandler/OpenSubtitlesHandler.csproj | 6 ++ 23 files changed, 279 insertions(+), 101 deletions(-) (limited to 'OpenSubtitlesHandler') diff --git a/MediaBrowser.Api/DisplayPreferencesService.cs b/MediaBrowser.Api/DisplayPreferencesService.cs index 16aafafb0e..206f5bf7b7 100644 --- a/MediaBrowser.Api/DisplayPreferencesService.cs +++ b/MediaBrowser.Api/DisplayPreferencesService.cs @@ -1,5 +1,4 @@ -using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller.Persistence; +using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Serialization; using ServiceStack; @@ -79,14 +78,6 @@ namespace MediaBrowser.Api { var result = _displayPreferencesManager.GetDisplayPreferences(request.Id, request.UserId, request.Client); - if (result == null) - { - result = new DisplayPreferences - { - Id = request.Id - }; - } - return ToOptimizedSerializedResultUsingCache(result); } diff --git a/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs b/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs index c143635bfa..0d61c2ce12 100644 --- a/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs +++ b/MediaBrowser.Api/ScheduledTasks/ScheduledTasksWebSocketListener.cs @@ -1,5 +1,6 @@ using MediaBrowser.Common.Net; using MediaBrowser.Common.ScheduledTasks; +using MediaBrowser.Model.Events; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Tasks; using System.Collections.Generic; @@ -11,7 +12,7 @@ namespace MediaBrowser.Api.ScheduledTasks /// /// Class ScheduledTasksWebSocketListener /// - public class ScheduledTasksWebSocketListener : BasePeriodicWebSocketListener, object> + public class ScheduledTasksWebSocketListener : BasePeriodicWebSocketListener, WebSocketListenerState> { /// /// Gets or sets the task manager. @@ -37,6 +38,26 @@ namespace MediaBrowser.Api.ScheduledTasks : base(logger) { TaskManager = taskManager; + + TaskManager.TaskExecuting += TaskManager_TaskExecuting; + TaskManager.TaskCompleted += TaskManager_TaskCompleted; + } + + void TaskManager_TaskCompleted(object sender, TaskCompletionEventArgs e) + { + SendData(true); + e.Task.TaskProgress -= Argument_TaskProgress; + } + + void TaskManager_TaskExecuting(object sender, GenericEventArgs e) + { + SendData(true); + e.Argument.TaskProgress += Argument_TaskProgress; + } + + void Argument_TaskProgress(object sender, GenericEventArgs e) + { + SendData(false); } /// @@ -44,12 +65,20 @@ namespace MediaBrowser.Api.ScheduledTasks /// /// The state. /// Task{IEnumerable{TaskInfo}}. - protected override Task> GetDataToSend(object state) + protected override Task> GetDataToSend(WebSocketListenerState state) { return Task.FromResult(TaskManager.ScheduledTasks .OrderBy(i => i.Name) .Select(ScheduledTaskHelpers.GetTaskInfo) .Where(i => !i.IsHidden)); } + + protected override bool SendOnTimer + { + get + { + return false; + } + } } } diff --git a/MediaBrowser.Api/WebSocket/LogFileWebSocketListener.cs b/MediaBrowser.Api/WebSocket/LogFileWebSocketListener.cs index 7fbea2f401..46dabb0420 100644 --- a/MediaBrowser.Api/WebSocket/LogFileWebSocketListener.cs +++ b/MediaBrowser.Api/WebSocket/LogFileWebSocketListener.cs @@ -133,7 +133,7 @@ namespace MediaBrowser.Api.WebSocket /// /// Class LogFileWebSocketState /// - public class LogFileWebSocketState + public class LogFileWebSocketState : WebSocketListenerState { /// /// Gets or sets the last log file path. diff --git a/MediaBrowser.Api/WebSocket/SessionInfoWebSocketListener.cs b/MediaBrowser.Api/WebSocket/SessionInfoWebSocketListener.cs index 721d8976b2..a16ea863f0 100644 --- a/MediaBrowser.Api/WebSocket/SessionInfoWebSocketListener.cs +++ b/MediaBrowser.Api/WebSocket/SessionInfoWebSocketListener.cs @@ -11,7 +11,7 @@ namespace MediaBrowser.Api.WebSocket /// /// Class SessionInfoWebSocketListener /// - class SessionInfoWebSocketListener : BasePeriodicWebSocketListener, object> + class SessionInfoWebSocketListener : BasePeriodicWebSocketListener, WebSocketListenerState> { /// /// Gets the name. @@ -43,7 +43,7 @@ namespace MediaBrowser.Api.WebSocket /// /// The state. /// Task{SystemInfo}. - protected override Task> GetDataToSend(object state) + protected override Task> GetDataToSend(WebSocketListenerState state) { return Task.FromResult(_sessionManager.Sessions.Where(i => i.IsActive).Select(_sessionManager.GetSessionInfoDto)); } diff --git a/MediaBrowser.Api/WebSocket/SystemInfoWebSocketListener.cs b/MediaBrowser.Api/WebSocket/SystemInfoWebSocketListener.cs index 62e642c927..2940bcef06 100644 --- a/MediaBrowser.Api/WebSocket/SystemInfoWebSocketListener.cs +++ b/MediaBrowser.Api/WebSocket/SystemInfoWebSocketListener.cs @@ -9,7 +9,7 @@ namespace MediaBrowser.Api.WebSocket /// /// Class SystemInfoWebSocketListener /// - public class SystemInfoWebSocketListener : BasePeriodicWebSocketListener + public class SystemInfoWebSocketListener : BasePeriodicWebSocketListener { /// /// Gets the name. @@ -41,7 +41,7 @@ namespace MediaBrowser.Api.WebSocket /// /// The state. /// Task{SystemInfo}. - protected override Task GetDataToSend(object state) + protected override Task GetDataToSend(WebSocketListenerState state) { return Task.FromResult(_appHost.GetSystemInfo()); } diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index d7d0cb886e..68222d8436 100644 --- a/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/MediaBrowser.Common.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -1,6 +1,8 @@ using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Events; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.ScheduledTasks; +using MediaBrowser.Model.Events; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Tasks; @@ -18,6 +20,8 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks /// public class ScheduledTaskWorker : IScheduledTaskWorker { + public event EventHandler> TaskProgress; + /// /// Gets or sets the scheduled task. /// @@ -344,13 +348,13 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks throw new InvalidOperationException("Cannot execute a Task that is already running"); } + var progress = new Progress(); + CurrentCancellationTokenSource = new CancellationTokenSource(); Logger.Info("Executing {0}", Name); - ((TaskManager)TaskManager).OnTaskExecuting(ScheduledTask); - - var progress = new Progress(); + ((TaskManager)TaskManager).OnTaskExecuting(this); progress.ProgressChanged += progress_ProgressChanged; @@ -412,6 +416,12 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks void progress_ProgressChanged(object sender, double e) { CurrentProgress = e; + + EventHelper.FireEventIfNotNull(TaskProgress, this, new GenericEventArgs + { + Argument = e + + }, Logger); } /// @@ -546,7 +556,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks LastExecutionResult = result; - ((TaskManager) TaskManager).OnTaskCompleted(ScheduledTask, result); + ((TaskManager)TaskManager).OnTaskCompleted(this, result); } /// diff --git a/MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs b/MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs index cead5de04b..5aec39b89c 100644 --- a/MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs +++ b/MediaBrowser.Common.Implementations/ScheduledTasks/TaskManager.cs @@ -17,8 +17,8 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks /// public class TaskManager : ITaskManager { - public event EventHandler TaskExecuting; - public event EventHandler> TaskCompleted; + public event EventHandler> TaskExecuting; + public event EventHandler TaskCompleted; /// /// Gets the list of Scheduled Tasks @@ -125,7 +125,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks // If it's idle just execute immediately if (task.State == TaskState.Idle) { - ((ScheduledTaskWorker)task).Execute(); + Execute(task); return; } @@ -149,7 +149,8 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks { var myTasks = ScheduledTasks.ToList(); - myTasks.AddRange(tasks.Select(t => new ScheduledTaskWorker(t, ApplicationPaths, this, JsonSerializer, Logger))); + var list = tasks.ToList(); + myTasks.AddRange(list.Select(t => new ScheduledTaskWorker(t, ApplicationPaths, this, JsonSerializer, Logger))); ScheduledTasks = myTasks.ToArray(); } @@ -189,9 +190,13 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks /// Called when [task executing]. /// /// The task. - internal void OnTaskExecuting(IScheduledTask task) + internal void OnTaskExecuting(IScheduledTaskWorker task) { - EventHelper.QueueEventIfNotNull(TaskExecuting, task, EventArgs.Empty, Logger); + EventHelper.QueueEventIfNotNull(TaskExecuting, this, new GenericEventArgs + { + Argument = task + + }, Logger); } /// @@ -199,9 +204,15 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks /// /// The task. /// The result. - internal void OnTaskCompleted(IScheduledTask task, TaskResult result) + internal void OnTaskCompleted(IScheduledTaskWorker task, TaskResult result) { - EventHelper.QueueEventIfNotNull(TaskCompleted, task, new GenericEventArgs { Argument = result }, Logger); + EventHelper.QueueEventIfNotNull(TaskCompleted, task, new TaskCompletionEventArgs + { + Result = result, + Task = task + + }, Logger); + ExecuteQueuedTasks(); } @@ -219,7 +230,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks if (scheduledTask.State == TaskState.Idle) { - ((ScheduledTaskWorker)scheduledTask).Execute(); + Execute(scheduledTask); _taskQueue.Remove(type); } diff --git a/MediaBrowser.Common/Net/BasePeriodicWebSocketListener.cs b/MediaBrowser.Common/Net/BasePeriodicWebSocketListener.cs index 33d3f368b6..a2af3707be 100644 --- a/MediaBrowser.Common/Net/BasePeriodicWebSocketListener.cs +++ b/MediaBrowser.Common/Net/BasePeriodicWebSocketListener.cs @@ -15,7 +15,7 @@ namespace MediaBrowser.Common.Net /// The type of the T return data type. /// The type of the T state type. public abstract class BasePeriodicWebSocketListener : IWebSocketListener, IDisposable - where TStateType : class, new() + where TStateType : WebSocketListenerState, new() where TReturnDataType : class { /// @@ -83,7 +83,15 @@ namespace MediaBrowser.Common.Net } protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); - + + protected virtual bool SendOnTimer + { + get + { + return true; + } + } + /// /// Starts sending messages over a web socket /// @@ -99,9 +107,15 @@ namespace MediaBrowser.Common.Net Logger.Info("{1} Begin transmitting over websocket to {0}", message.Connection.RemoteEndPoint, GetType().Name); - var timer = new Timer(TimerCallback, message.Connection, Timeout.Infinite, Timeout.Infinite); + var timer = SendOnTimer ? + new Timer(TimerCallback, message.Connection, Timeout.Infinite, Timeout.Infinite) : + null; - var state = new TStateType(); + var state = new TStateType + { + IntervalMs = periodMs, + InitialDelayMs = dueTimeMs + }; var semaphore = new SemaphoreSlim(1, 1); @@ -110,14 +124,17 @@ namespace MediaBrowser.Common.Net ActiveConnections.Add(new Tuple(message.Connection, cancellationTokenSource, timer, state, semaphore)); } - timer.Change(TimeSpan.FromMilliseconds(dueTimeMs), TimeSpan.FromMilliseconds(periodMs)); + if (timer != null) + { + timer.Change(TimeSpan.FromMilliseconds(dueTimeMs), TimeSpan.FromMilliseconds(periodMs)); + } } /// /// Timers the callback. /// /// The state. - private async void TimerCallback(object state) + private void TimerCallback(object state) { var connection = (IWebSocketConnection)state; @@ -139,11 +156,50 @@ namespace MediaBrowser.Common.Net return; } + SendData(tuple); + } + + protected void SendData(bool force) + { + List> tuples; + + lock (ActiveConnections) + { + tuples = ActiveConnections + .Where(c => + { + if (c.Item1.State == WebSocketState.Open && !c.Item2.IsCancellationRequested) + { + var state = c.Item4; + + if (force || (DateTime.UtcNow - state.DateLastSendUtc).TotalMilliseconds >= state.IntervalMs) + { + return true; + } + } + + return false; + }) + .ToList(); + } + + foreach (var tuple in tuples) + { + SendData(tuple); + } + } + + private async void SendData(Tuple tuple) + { + var connection = tuple.Item1; + try { await tuple.Item5.WaitAsync(tuple.Item2.Token).ConfigureAwait(false); - var data = await GetDataToSend(tuple.Item4).ConfigureAwait(false); + var state = tuple.Item4; + + var data = await GetDataToSend(state).ConfigureAwait(false); if (data != null) { @@ -153,6 +209,8 @@ namespace MediaBrowser.Common.Net Data = data }, tuple.Item2.Token).ConfigureAwait(false); + + state.DateLastSendUtc = DateTime.UtcNow; } tuple.Item5.Release(); @@ -196,13 +254,18 @@ namespace MediaBrowser.Common.Net { Logger.Info("{1} stop transmitting over websocket to {0}", connection.Item1.RemoteEndPoint, GetType().Name); - try - { - connection.Item3.Dispose(); - } - catch (ObjectDisposedException) + var timer = connection.Item3; + + if (timer != null) { + try + { + timer.Dispose(); + } + catch (ObjectDisposedException) + { + } } try @@ -212,7 +275,7 @@ namespace MediaBrowser.Common.Net } catch (ObjectDisposedException) { - + } try @@ -223,7 +286,7 @@ namespace MediaBrowser.Common.Net { } - + ActiveConnections.Remove(connection); } @@ -253,4 +316,11 @@ namespace MediaBrowser.Common.Net Dispose(true); } } + + public class WebSocketListenerState + { + public DateTime DateLastSendUtc { get; set; } + public long InitialDelayMs { get; set; } + public long IntervalMs { get; set; } + } } diff --git a/MediaBrowser.Common/ScheduledTasks/IScheduledTaskWorker.cs b/MediaBrowser.Common/ScheduledTasks/IScheduledTaskWorker.cs index d0d363df76..f50bd9e76e 100644 --- a/MediaBrowser.Common/ScheduledTasks/IScheduledTaskWorker.cs +++ b/MediaBrowser.Common/ScheduledTasks/IScheduledTaskWorker.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Model.Tasks; +using MediaBrowser.Model.Events; +using MediaBrowser.Model.Tasks; using System; using System.Collections.Generic; @@ -9,6 +10,11 @@ namespace MediaBrowser.Common.ScheduledTasks /// public interface IScheduledTaskWorker : IDisposable { + /// + /// Occurs when [task progress]. + /// + event EventHandler> TaskProgress; + /// /// Gets or sets the scheduled task. /// diff --git a/MediaBrowser.Common/ScheduledTasks/ITaskManager.cs b/MediaBrowser.Common/ScheduledTasks/ITaskManager.cs index 38548801b2..01dc355c33 100644 --- a/MediaBrowser.Common/ScheduledTasks/ITaskManager.cs +++ b/MediaBrowser.Common/ScheduledTasks/ITaskManager.cs @@ -1,5 +1,4 @@ using MediaBrowser.Model.Events; -using MediaBrowser.Model.Tasks; using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -50,7 +49,7 @@ namespace MediaBrowser.Common.ScheduledTasks void Cancel(IScheduledTaskWorker task); Task Execute(IScheduledTaskWorker task); - event EventHandler TaskExecuting; - event EventHandler> TaskCompleted; + event EventHandler> TaskExecuting; + event EventHandler TaskCompleted; } } \ No newline at end of file diff --git a/MediaBrowser.Common/ScheduledTasks/TaskCompletionEventArgs.cs b/MediaBrowser.Common/ScheduledTasks/TaskCompletionEventArgs.cs index 8aab6b50d4..2974806d02 100644 --- a/MediaBrowser.Common/ScheduledTasks/TaskCompletionEventArgs.cs +++ b/MediaBrowser.Common/ScheduledTasks/TaskCompletionEventArgs.cs @@ -5,7 +5,7 @@ namespace MediaBrowser.Common.ScheduledTasks { public class TaskCompletionEventArgs : EventArgs { - public IScheduledTask Task { get; set; } + public IScheduledTaskWorker Task { get; set; } public TaskResult Result { get; set; } } diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index 19287b0cbb..a67d69f79f 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -85,13 +85,7 @@ - - - - This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - +