├── SjUpdater ├── Resources │ ├── Entypo.ttf │ ├── icon3.ico │ ├── Entypo-license.txt │ └── WindowsIcons-license.txt ├── Utils │ ├── StaticInstance.cs │ ├── Native.cs │ ├── timehelper.cs │ ├── UploadCache.cs │ ├── RegexValidationRule.cs │ ├── StringToFaviconConverter.cs │ ├── GridViewWidthCalulationMultiConverter.cs │ ├── ExtensionMethods.cs │ ├── ThreadPool.cs │ ├── PropertyChangedImpl.cs │ ├── GlobalMutex.cs │ ├── SimpleCommand.cs │ ├── GridViewColumnVisibilityManager.cs │ ├── EnumText.cs │ ├── Crc64.cs │ ├── CachedImage.cs │ ├── Stats.cs │ └── FavIcon.cs ├── Model │ ├── ShowData.cs │ ├── DownloadData.cs │ ├── SeasonData.cs │ ├── UploadData.cs │ ├── ShowCategory.cs │ ├── FavEpisodeData.cs │ ├── FavSeasonData.cs │ ├── ClassDiagram1.cd │ └── ShowCategorySetting.cs ├── Provider │ ├── ProviderManager.cs │ ├── IProvider.cs │ └── TMDb.cs ├── packages.config ├── MultiDownloadSelectionCell.xaml.cs ├── MultiDownloadSelectionHeader.xaml.cs ├── App.config ├── App.xaml.cs ├── XML │ ├── CustomXmlAttributes.cs │ ├── CustomXmlSerializerBase.cs │ └── XmlSerialization.cs ├── MultiDownloadSelectionCell.xaml ├── App.xaml ├── SpecialDownloadList.xaml.cs ├── NotificationBalloon.xaml.cs ├── DownloadPopupList.xaml.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── app.manifest │ └── Resources.resx ├── Updater │ ├── UpdateWindow.xaml │ ├── UpdaterViewModel.cs │ └── UpdateWindow.xaml.cs ├── MultiDownloadSelectionHeader.xaml ├── ViewModel │ ├── MultiSelectionViewModel.cs │ ├── SeasonViewModel.cs │ ├── MainWindowViewModel.cs │ └── ShowTileViewModel.cs ├── SpecialDownloadList.xaml ├── MultiDownloadSelectionGrid.xaml ├── NotificationBalloon.xaml └── DownloadPopupList.xaml ├── updaterhasher ├── App.config ├── Properties │ └── AssemblyInfo.cs ├── Program.cs └── updaterhasher.csproj ├── SjUpdater.sln.DotSettings ├── README.md ├── SjUpdater.sln └── .gitignore /SjUpdater/Resources/Entypo.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dreamcooled/sjupdater/HEAD/SjUpdater/Resources/Entypo.ttf -------------------------------------------------------------------------------- /SjUpdater/Resources/icon3.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dreamcooled/sjupdater/HEAD/SjUpdater/Resources/icon3.ico -------------------------------------------------------------------------------- /SjUpdater/Resources/Entypo-license.txt: -------------------------------------------------------------------------------- 1 | Entypo (http://www.entypo.com/) is created by Daniel Bruce and released under the Creative Commons, Share Alike/Attribution license. 2 | 3 | http://creativecommons.org/licenses/by-sa/3.0/ -------------------------------------------------------------------------------- /updaterhasher/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /SjUpdater/Utils/StaticInstance.cs: -------------------------------------------------------------------------------- 1 | namespace SjUpdater.Utils 2 | { 3 | public static class StaticInstance 4 | { 5 | public static ThreadPool ThreadPool { get; } 6 | 7 | static StaticInstance() 8 | { 9 | ThreadPool = new ThreadPool(); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /SjUpdater/Utils/Native.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace SjUpdater.Utils 4 | { 5 | static public class Native 6 | { 7 | [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)] 8 | static public extern int memcmp(byte[] b1, byte[] b2, long count); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /SjUpdater/Model/ShowData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SjUpdater.Model 4 | { 5 | public class ShowData 6 | 7 | { 8 | public ShowData() 9 | { 10 | Name = ""; 11 | Url = ""; 12 | } 13 | public String Name { get; set; } 14 | public String Url { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SjUpdater/Model/DownloadData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace SjUpdater.Model 5 | { 6 | public class DownloadData 7 | { 8 | public DownloadData() 9 | { 10 | Title = ""; 11 | Upload = null; 12 | Links = new Dictionary(); 13 | } 14 | public String Title { get; set; } 15 | public Dictionary Links { get; internal set; } 16 | public UploadData Upload { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /SjUpdater/Utils/timehelper.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | 4 | namespace SjUpdater.Utils 5 | { 6 | public static class timehelper 7 | { 8 | public static List dateTimes = new List(); 9 | 10 | private static Stopwatch sw; 11 | 12 | static timehelper() 13 | { 14 | sw = Stopwatch.StartNew(); 15 | } 16 | 17 | public static void Add() 18 | { 19 | dateTimes.Add(sw.Elapsed.TotalSeconds); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /SjUpdater.sln.DotSettings: -------------------------------------------------------------------------------- 1 | 2 | True 3 | STP -------------------------------------------------------------------------------- /SjUpdater/Provider/ProviderManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace SjUpdater.Provider 8 | { 9 | public static class ProviderManager 10 | { 11 | private static readonly IProvider Provider; 12 | 13 | static ProviderManager() 14 | { 15 | Provider = new TMDb(); 16 | } 17 | 18 | public static IProvider GetProvider() 19 | { 20 | return Provider; 21 | } 22 | 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /SjUpdater/Model/SeasonData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SjUpdater.Model 4 | { 5 | public class SeasonData 6 | { 7 | public SeasonData() 8 | { 9 | Title = ""; 10 | Description = ""; 11 | Url = ""; 12 | CoverUrl = ""; 13 | Show = null; 14 | } 15 | public String Title { get; set; } 16 | public String Description { get; set; } 17 | public String Url { get; set; } 18 | public String CoverUrl { get; set; } 19 | public ShowData Show { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SjUpdater/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /SjUpdater/MultiDownloadSelectionCell.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Navigation; 14 | using System.Windows.Shapes; 15 | 16 | namespace SjUpdater 17 | { 18 | /// 19 | /// Interaction logic for MultiDownloadSelectionCell.xaml 20 | /// 21 | public partial class MultiDownloadSelectionCell : UserControl 22 | { 23 | public MultiDownloadSelectionCell() 24 | { 25 | InitializeComponent(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /SjUpdater/MultiDownloadSelectionHeader.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Navigation; 14 | using System.Windows.Shapes; 15 | 16 | namespace SjUpdater 17 | { 18 | /// 19 | /// Interaction logic for MultiDownloadSelectionHeader.xaml 20 | /// 21 | public partial class MultiDownloadSelectionHeader : UserControl 22 | { 23 | public MultiDownloadSelectionHeader() 24 | { 25 | InitializeComponent(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /SjUpdater/Utils/UploadCache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using SjUpdater.Model; 8 | 9 | namespace SjUpdater.Utils 10 | { 11 | public class UploadCache 12 | { 13 | private readonly ConcurrentDictionary _uploadCache = new ConcurrentDictionary(); 14 | public UploadData GetUniqueUploadData(UploadData u) 15 | { 16 | if (u == null) return null; 17 | int k = u.GetHashCode(); 18 | UploadData v; 19 | if (_uploadCache.TryGetValue(k, out v) && v.Equals(u)) 20 | { 21 | return v; 22 | } 23 | _uploadCache.TryAdd(k, u); 24 | return u; 25 | } 26 | 27 | 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /SjUpdater/Utils/RegexValidationRule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Text.RegularExpressions; 4 | using System.Windows.Controls; 5 | 6 | namespace SjUpdater.Utils 7 | { 8 | public class RegexValidationRule : ValidationRule 9 | { 10 | public override ValidationResult Validate(object value, CultureInfo cultureInfo) 11 | { 12 | String s = (String) value; 13 | if (!string.IsNullOrWhiteSpace(s)) 14 | { 15 | 16 | try 17 | { 18 | Regex.Match("", s); 19 | } 20 | catch (ArgumentException ex) 21 | { 22 | return new ValidationResult(false, ex.Message); 23 | } 24 | 25 | } 26 | return new ValidationResult(true, "Ok"); 27 | 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /SjUpdater/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /SjUpdater/Utils/StringToFaviconConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows.Data; 8 | using System.Windows.Media; 9 | 10 | namespace SjUpdater.Utils 11 | { 12 | public class StringToFaviconConverter : IValueConverter 13 | { 14 | public object Convert(object value, Type targetType, 15 | object parameter, CultureInfo culture) 16 | { 17 | if (targetType != typeof(FavIcon) || value.GetType() != typeof(string)) 18 | { 19 | // throw new ArgumentException(); 20 | } 21 | return new FavIcon(value as String); 22 | } 23 | 24 | public object ConvertBack(object value, Type targetType, 25 | object parameter, CultureInfo culture) 26 | { 27 | return null; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /SjUpdater/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net; 4 | using System.Reflection; 5 | using System.Windows; 6 | using SjUpdater.Utils; 7 | 8 | namespace SjUpdater 9 | { 10 | /// 11 | /// Interaktionslogik für "App.xaml" 12 | /// 13 | public partial class App : Application 14 | { 15 | protected override void OnStartup(StartupEventArgs e) 16 | { 17 | Directory.SetCurrentDirectory(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)); 18 | 19 | if (!GlobalMutex.TryGetMutex()) { 20 | Environment.Exit(0); 21 | } else { 22 | base.OnStartup(e); 23 | } 24 | 25 | ServicePointManager.DefaultConnectionLimit = 25; 26 | } 27 | 28 | protected override void OnExit(ExitEventArgs e) 29 | { 30 | GlobalMutex.ReleaseMutex(); 31 | base.OnExit(e); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /SjUpdater/XML/CustomXmlAttributes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SjUpdater.XML 4 | { 5 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] 6 | public class XmlIgnoreBaseTypeAttribute : Attribute 7 | { 8 | } 9 | 10 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] 11 | public class CustomXmlSerializationOptionsAttribute : Attribute 12 | { 13 | public CustomXmlSerializer.SerializationOptions SerializationOptions = new CustomXmlSerializer.SerializationOptions(); 14 | 15 | public CustomXmlSerializationOptionsAttribute(bool useTypeCache, bool useGraphSerialization) 16 | { 17 | SerializationOptions.UseTypeCache = useTypeCache; 18 | SerializationOptions.UseGraphSerialization = useGraphSerialization; 19 | } 20 | } 21 | 22 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] 23 | public class XmlSerializeAsCustomTypeAttribute : Attribute 24 | { 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /SjUpdater/MultiDownloadSelectionCell.xaml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /SjUpdater/App.xaml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![Github All Releases](https://img.shields.io/github/downloads/dreamcooled/sjupdater/total.svg?style=flat)](https://github.com/Dreamcooled/sjupdater/releases) 3 | [![GitHub license](https://img.shields.io/github/license/dreamcooled/sjupdater.svg?style=flat)](https://github.com/Dreamcooled/sjupdater/blob/master/LICENSE.md) 4 | [![GitHub issues](https://img.shields.io/github/issues/dreamcooled/sjupdater.svg?style=flat)](https://github.com/Dreamcooled/sjupdater/issues) 5 | # SjUpdater 6 | 7 | An Updater and Linkaggregator for Serienjunkies.org written in C#/WPF. 8 | Any contribution would be highly appreciated. 9 | 10 | 11 | 12 | #### Download 13 | Binaries of current version (needs .NET 4.5): [![GitHub release](https://img.shields.io/github/release/dreamcooled/sjupdater.svg?style=flat)](https://github.com/Dreamcooled/sjupdater/releases/latest) 14 | 15 | 16 | 17 | #### Screenshots 18 | 19 | ![Main Screen](http://fs1.directupload.net/images/150831/xdoo57z4.png) 20 | ![Show Screen](http://fs2.directupload.net/images/141222/oq7pq2mv.png) 21 | ![Show Screen 2](http://fs2.directupload.net/images/141222/yhzxtrjk.png) 22 | ![Settings Screen](http://fs1.directupload.net/images/141222/plp3of5m.png) 23 | ![Notification](http://s7.directupload.net/images/140810/wf7jemqk.png) 24 | 25 | 26 | -------------------------------------------------------------------------------- /SjUpdater/Utils/GridViewWidthCalulationMultiConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | 10 | //Code taken from: http://stackoverflow.com/questions/5573152/how-to-resize-a-certain-control-based-on-window-size-in-wpf#5573895 11 | 12 | namespace SjUpdater.Utils 13 | { 14 | public class GridViewWidthCalulationMultiConverter : IMultiValueConverter 15 | { 16 | public object Convert(object[] values, Type targetType, 17 | object parameter, CultureInfo culture) 18 | { 19 | // do some sort of calculation 20 | double totalWindowWidth; 21 | double otherColumnsTotalWidth = 0; 22 | double.TryParse(values[0].ToString(), out totalWindowWidth); 23 | var arrayOfColumns = values[1] as IList; 24 | 25 | for (int i = 0; i < arrayOfColumns.Count - 1; i++) 26 | { 27 | otherColumnsTotalWidth += arrayOfColumns[i].ActualWidth; 28 | } 29 | 30 | return (totalWindowWidth - otherColumnsTotalWidth) < 0 ? 31 | 0 : (totalWindowWidth - otherColumnsTotalWidth); 32 | } 33 | 34 | public object[] ConvertBack(object value, Type[] targetTypes, 35 | object parameter, CultureInfo culture) 36 | { 37 | throw new NotImplementedException(); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /updaterhasher/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // Allgemeine Informationen über eine Assembly werden über die folgenden 6 | // Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, 7 | // die mit einer Assembly verknüpft sind. 8 | [assembly: AssemblyTitle("updaterhasher")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("updaterhasher")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar 18 | // für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von 19 | // COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest. 20 | [assembly: ComVisible(false)] 21 | 22 | // Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird 23 | [assembly: Guid("da482963-3395-42e5-bde9-a87bd29af0f6")] 24 | 25 | // Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: 26 | // 27 | // Hauptversion 28 | // Nebenversion 29 | // Buildnummer 30 | // Revision 31 | // 32 | // Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern 33 | // übernehmen, indem Sie "*" eingeben: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /updaterhasher/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Security.Cryptography; 4 | using System.Threading; 5 | using System.Windows.Forms; 6 | 7 | namespace updaterhasher 8 | { 9 | internal class Program 10 | { 11 | [STAThread] 12 | private static void Main(string[] args) 13 | { 14 | Console.ForegroundColor = ConsoleColor.Red; 15 | Console.WriteLine("Warning: Filenames cannot have spaces yet"); 16 | Console.WriteLine("Warning: Sub Directories are not support yet"); 17 | 18 | Console.ResetColor(); 19 | Console.WriteLine("\nDrag'n Drop a Folder"); 20 | string path = Console.ReadLine().Replace("\"", ""); 21 | 22 | string[] files = Directory.GetFiles(path); 23 | 24 | string text = ""; 25 | 26 | Console.WriteLine(); 27 | 28 | foreach (var file in files) 29 | { 30 | MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider(); 31 | 32 | byte[] hashBytes; 33 | using (FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read)) 34 | { 35 | hashBytes = md5.ComputeHash(fs); 36 | } 37 | 38 | string filetext = Path.GetFileName(file) + ":" + BitConverter.ToString(hashBytes).Replace("-", "").ToLower(); 39 | 40 | Console.WriteLine(filetext); 41 | 42 | text += filetext + "\n"; 43 | } 44 | 45 | Clipboard.SetText(text); 46 | Console.WriteLine("\nText copied to Clipboard"); 47 | Thread.Sleep(3500); 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /SjUpdater.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27130.2027 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SjUpdater", "SjUpdater\SjUpdater.csproj", "{96123159-7974-4FEB-BD8C-7AB562DF3E01}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "updaterhasher", "updaterhasher\updaterhasher.csproj", "{89B40DD8-7928-4B21-B792-89AF030F6B55}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {96123159-7974-4FEB-BD8C-7AB562DF3E01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {96123159-7974-4FEB-BD8C-7AB562DF3E01}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {96123159-7974-4FEB-BD8C-7AB562DF3E01}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {96123159-7974-4FEB-BD8C-7AB562DF3E01}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {89B40DD8-7928-4B21-B792-89AF030F6B55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {89B40DD8-7928-4B21-B792-89AF030F6B55}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {89B40DD8-7928-4B21-B792-89AF030F6B55}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {89B40DD8-7928-4B21-B792-89AF030F6B55}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {A06120FB-BCF3-4AD1-9400-C5601E971262} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /SjUpdater/Utils/ExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | 5 | namespace SjUpdater.Utils 6 | { 7 | public static class ExtensionMethods 8 | { 9 | /// 10 | /// Checks whether 2 byte arrays equals 11 | /// 12 | /// 13 | /// 14 | /// 15 | public static bool Memcmp(this byte[] array, byte[] array2) 16 | { 17 | if (array.Length != array2.Length) 18 | return false; 19 | 20 | return Native.memcmp(array, array2, array.Length) == 0; 21 | } 22 | 23 | public static Comparer CreateMultiple(params Comparer[] comparers ) 24 | { 25 | return Comparer.Create(delegate(T x, T y) 26 | { 27 | foreach (var comparer in comparers) 28 | { 29 | int i = comparer.Compare(x, y); 30 | if (i != 0) return i; 31 | } 32 | return 0; 33 | }); 34 | } 35 | 36 | public static void Sort(this ObservableCollection source, Comparer comparer, bool desc = false) 37 | { 38 | if (source == null) return; 39 | 40 | for (int i = source.Count - 1; i >= 0; i--) 41 | { 42 | for (int j = 1; j <= i; j++) 43 | { 44 | TSource o1 = source[j - 1]; 45 | TSource o2 = source[j]; 46 | int comparison = comparer.Compare(o1, o2); 47 | if (desc && comparison < 0) 48 | source.Move(j, j - 1); 49 | else if (!desc && comparison > 0) 50 | source.Move(j - 1, j); 51 | } 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /SjUpdater/SpecialDownloadList.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Controls; 9 | using System.Windows.Data; 10 | using System.Windows.Documents; 11 | using System.Windows.Input; 12 | using System.Windows.Media; 13 | using System.Windows.Media.Imaging; 14 | using System.Windows.Navigation; 15 | using System.Windows.Shapes; 16 | 17 | namespace SjUpdater 18 | { 19 | /// 20 | /// Interaction logic for SpecialDownloadList.xaml 21 | /// 22 | public partial class SpecialDownloadList : UserControl 23 | { 24 | public SpecialDownloadList() 25 | { 26 | InitializeComponent(); 27 | } 28 | public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", 29 | typeof(IEnumerable), typeof(SpecialDownloadList), new FrameworkPropertyMetadata(null)); 30 | 31 | public IEnumerable ItemsSource 32 | { 33 | get 34 | { 35 | return (IEnumerable)GetValue(SpecialDownloadList.ItemsSourceProperty); 36 | } 37 | set 38 | { 39 | this.SetValue(SpecialDownloadList.ItemsSourceProperty, value); 40 | } 41 | } 42 | 43 | public static readonly DependencyProperty DownloadCommandProperty = DependencyProperty.Register("DownloadCommand", 44 | typeof(ICommand), typeof(SpecialDownloadList), new FrameworkPropertyMetadata(null)); 45 | 46 | public ICommand DownloadCommand 47 | { 48 | get 49 | { 50 | return (ICommand)GetValue(SpecialDownloadList.DownloadCommandProperty); 51 | } 52 | set 53 | { 54 | this.SetValue(SpecialDownloadList.DownloadCommandProperty, value); 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /SjUpdater/Model/UploadData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace SjUpdater.Model 6 | { 7 | [Flags] 8 | public enum UploadLanguage 9 | { 10 | German = 1, 11 | English = 2, 12 | Any = German + English 13 | } 14 | public class UploadData 15 | { 16 | public UploadData() 17 | { 18 | Uploader = ""; 19 | Format = ""; 20 | Size = ""; 21 | Runtime = ""; 22 | Language = 0; 23 | Subbed = false; 24 | Season = null; 25 | Favorized = false; 26 | } 27 | 28 | public String Uploader { get; set; } 29 | public String Format { get; set; } 30 | public String Size { get; set; } 31 | public String Runtime { get; set; } 32 | public UploadLanguage Language { get; set; } 33 | public SeasonData Season { get; set; } 34 | public bool Subbed { get; set; } 35 | 36 | public bool Favorized { get; set; } //Todo: move to Fav* class, since it's user data 37 | 38 | public static IEnumerable LanguagesValues 39 | { 40 | get 41 | { 42 | return Enum.GetValues(typeof(UploadLanguage)) 43 | .Cast(); 44 | } 45 | } 46 | 47 | public override bool Equals(object obj) 48 | { 49 | var u2 = obj as UploadData; 50 | if (u2 == null) return false; 51 | return Uploader == u2.Uploader && Format == u2.Format && Size == u2.Size && Runtime == u2.Runtime && 52 | Language == u2.Language && 53 | (Season == u2.Season || (Season != null && u2.Season != null && Season.Url == u2.Season.Url)); 54 | } 55 | 56 | public override int GetHashCode() 57 | { 58 | return Uploader.GetHashCode() ^ Format.GetHashCode() ^ Size.GetHashCode() ^ Runtime.GetHashCode() ^ 59 | Language.GetHashCode() ^ ((Season==null)?0:Season.Url.GetHashCode()); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /SjUpdater/NotificationBalloon.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Shapes; 14 | using Hardcodet.Wpf.TaskbarNotification; 15 | using MahApps.Metro.Controls; 16 | using SjUpdater.Model; 17 | using SjUpdater.Utils; 18 | using SjUpdater.ViewModel; 19 | 20 | namespace SjUpdater 21 | { 22 | /// 23 | /// Interaktionslogik für NotificationBalloon.xaml 24 | /// 25 | public partial class NotificationBalloon :UserControl 26 | { 27 | public ICommand ShowClickedCommand { get; private set; } 28 | public NotificationBalloon(IEnumerable list) 29 | { 30 | InitializeComponent(); 31 | ShowClickedCommand = new SimpleCommand(OnShowViewClicked); 32 | ItemsControl.ItemsSource = list.Select(s => new ShowTileViewModel(s)); 33 | } 34 | 35 | public event ShowViewClickedDelegate ShowViewClicked; 36 | 37 | private void OnShowViewClicked(ShowTileViewModel obj) 38 | { 39 | if (ShowViewClicked != null) 40 | { 41 | ShowViewClicked(this, obj); 42 | } 43 | TaskbarIcon taskbarIcon = TaskbarIcon.GetParentTaskbarIcon(this); 44 | taskbarIcon.CloseBalloon(); 45 | } 46 | 47 | private void CloseBalloon(object sender, RoutedEventArgs e) 48 | { 49 | TaskbarIcon taskbarIcon = TaskbarIcon.GetParentTaskbarIcon(this); 50 | taskbarIcon.CloseBalloon(); 51 | } 52 | 53 | private void Grid_MouseEnter_1(object sender, MouseEventArgs e) 54 | { 55 | TaskbarIcon taskbarIcon = TaskbarIcon.GetParentTaskbarIcon(this); 56 | taskbarIcon.ResetBalloonCloseTimer(); 57 | } 58 | } 59 | 60 | public delegate void ShowViewClickedDelegate(object sender, ShowTileViewModel arg); 61 | } 62 | -------------------------------------------------------------------------------- /SjUpdater/Resources/WindowsIcons-license.txt: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | Please carefully understand the license and download the latest icons at ModernUIIcons.com. 4 | 5 | ## Understand Your Rights 6 | No Attribution and No Derived Works 7 | http://creativecommons.org/licenses/by-nd/3.0/ * 8 | 9 | - If your project is open source include this license file in the source. 10 | - Nothing is needed in the front facing project (UNLESS you 11 | are using any of the icons listed below in the attribution section). 12 | - Commercial use is not only allowed but encouraged. If it is an icon 13 | in the attribution list below, you still need to attribute those! 14 | - Do not distribute the entire package (I've allowed this dozens of 15 | times for open source projects, but email me first). 16 | 17 | ## Creator 18 | - Austin Andrews (@templarian) 19 | 20 | ## Contributor** 21 | - Jay Zawrotny (@JayZawrotny) 22 | - A Bunch 23 | - Oren Nachman 24 | - appbar.chevron.down 25 | - appbar.chevron.up 26 | - appbar.chevron.left 27 | - appbar.chevron.right 28 | 29 | ## Derived Works 30 | - Alex Peattie 31 | - Social: http://www.alexpeattie.com/projects/justvector_icons/ 32 | 33 | ## Attribution*** 34 | - Kris Vandermotten (@kvandermotten) 35 | - appbar.medical.pulse 36 | - Constantin Kichinsky (@kichinsky) 37 | - appbar.currency.rubles 38 | - appbar.currency.grivna 39 | - Massimo Savazzi (@msavazzi) 40 | - List of missing exported icons 41 | - Proletkult Graphik, from The Noun Project 42 | - appbar.draw.pen (inspired) 43 | - Olivier Guin, from The Noun Project 44 | - appbar.draw.marker 45 | - Gibran Bisio, from The Noun Project 46 | - appbar.draw.bucket 47 | Andrew Forrester, from The Noun Project 48 | - appbar.fingerprint 49 | 50 | * The license is for attribution, but this is not required. 51 | ** Developers and designers that emailed Templarian the source .design icons to be added into the package. PNGs also accepted, but may take longer to be added. 52 | *** Icons I've copied so closely you want to attribute them and are also under the CC license. 53 | 54 | Contact 55 | - http://templarian.com/ 56 | - admin[@]templarian[.]com 57 | 58 | * Does not apply to copyrighted logos 59 | - Skype 60 | - Facebook 61 | - Twitter 62 | - etc... 63 | -------------------------------------------------------------------------------- /SjUpdater/Utils/ThreadPool.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Linq; 4 | using System.Threading; 5 | using Amib.Threading; 6 | using Action = Amib.Threading.Action; 7 | 8 | namespace SjUpdater.Utils 9 | { 10 | public class ThreadPool 11 | { 12 | private volatile ConcurrentBag _threadPools = new ConcurrentBag(); 13 | private int _maxThreads; 14 | 15 | public int MaxThreads 16 | { 17 | get { return _maxThreads; } 18 | set 19 | { 20 | _maxThreads = value; 21 | foreach (var threadPool in _threadPools) 22 | { 23 | threadPool.MaxThreads = _maxThreads; 24 | } 25 | } 26 | } 27 | 28 | public ThreadPool(int maxWorkerThreads = 12) 29 | { 30 | _maxThreads = maxWorkerThreads; 31 | } 32 | 33 | private SmartThreadPool getPool(bool background, ThreadPriority priority, WorkItemPriority itemPriority) 34 | { 35 | var pool = _threadPools.FirstOrDefault(p => p.STPStartInfo.ThreadPriority == priority && p.STPStartInfo.WorkItemPriority == itemPriority && p.STPStartInfo.AreThreadsBackground == background); 36 | 37 | if (pool == null) 38 | { 39 | pool = new SmartThreadPool(new STPStartInfo 40 | { 41 | AreThreadsBackground = background, 42 | ThreadPriority = priority, 43 | WorkItemPriority = itemPriority, 44 | MaxWorkerThreads = _maxThreads 45 | }); 46 | pool.Start(); 47 | _threadPools.Add(pool); 48 | } 49 | 50 | return pool; 51 | } 52 | 53 | public IWorkItemResult QueueWorkItem(Action action, bool background = true, WorkItemPriority itemPriority = WorkItemPriority.Normal, ThreadPriority priority = ThreadPriority.Normal) 54 | { 55 | var pool = getPool(background, priority, itemPriority); 56 | return pool.QueueWorkItem(action, itemPriority); 57 | } 58 | 59 | public IWorkItemResult QueueWorkItem(Action action, T item, bool background = true, WorkItemPriority itemPriority = WorkItemPriority.Normal, ThreadPriority priority = ThreadPriority.Normal) 60 | { 61 | var pool = getPool(background, priority, itemPriority); 62 | return pool.QueueWorkItem(action, item, itemPriority); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /updaterhasher/updaterhasher.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {89B40DD8-7928-4B21-B792-89AF030F6B55} 8 | Exe 9 | Properties 10 | updaterhasher 11 | updaterhasher 12 | v4.7.1 13 | 512 14 | 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | 32 | 33 | prompt 34 | 4 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 56 | -------------------------------------------------------------------------------- /SjUpdater/DownloadPopupList.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Controls; 9 | using System.Windows.Data; 10 | using System.Windows.Documents; 11 | using System.Windows.Input; 12 | using System.Windows.Media; 13 | using System.Windows.Media.Imaging; 14 | using System.Windows.Navigation; 15 | using System.Windows.Shapes; 16 | 17 | namespace SjUpdater 18 | { 19 | /// 20 | /// Interaction logic for DownloadPopupList.xaml 21 | /// 22 | public partial class DownloadPopupList : UserControl 23 | { 24 | public DownloadPopupList() 25 | { 26 | InitializeComponent(); 27 | } 28 | 29 | public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", 30 | typeof (IEnumerable), typeof (DownloadPopupList), new FrameworkPropertyMetadata(null)); 31 | 32 | public IEnumerable ItemsSource 33 | { 34 | get 35 | { 36 | return (IEnumerable)GetValue(DownloadPopupList.ItemsSourceProperty); 37 | } 38 | set 39 | { 40 | this.SetValue(DownloadPopupList.ItemsSourceProperty, value); 41 | } 42 | } 43 | 44 | public static readonly DependencyProperty DownloadCommandProperty = DependencyProperty.Register("DownloadCommand", 45 | typeof(ICommand), typeof(DownloadPopupList), new FrameworkPropertyMetadata(null)); 46 | 47 | public ICommand DownloadCommand 48 | { 49 | get 50 | { 51 | return (ICommand)GetValue(DownloadPopupList.DownloadCommandProperty); 52 | } 53 | set 54 | { 55 | this.SetValue(DownloadPopupList.DownloadCommandProperty, value); 56 | } 57 | } 58 | 59 | 60 | public static readonly DependencyProperty ShowFavColumnProperty = DependencyProperty.Register("ShowFavColumn", 61 | typeof(bool), typeof(DownloadPopupList), new FrameworkPropertyMetadata(true)); 62 | 63 | public bool ShowFavColumn 64 | { 65 | get 66 | { 67 | return (bool)GetValue(DownloadPopupList.ShowFavColumnProperty); 68 | } 69 | set 70 | { 71 | this.SetValue(DownloadPopupList.ShowFavColumnProperty, value); 72 | } 73 | } 74 | 75 | 76 | 77 | 78 | 79 | } 80 | 81 | 82 | 83 | 84 | } 85 | -------------------------------------------------------------------------------- /SjUpdater/Model/ShowCategory.cs: -------------------------------------------------------------------------------- 1 | using SjUpdater.ViewModel; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Collections.ObjectModel; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows.Threading; 9 | 10 | namespace SjUpdater.Model 11 | { 12 | public class ShowCategory 13 | { 14 | private readonly Dispatcher _dispatcher; 15 | 16 | public ShowCategory() 17 | { 18 | _dispatcher = Dispatcher.CurrentDispatcher; 19 | } 20 | 21 | 22 | public static readonly List DefaultSettings = new List() 23 | { 24 | new ShowCategorySetting("new",CategoryOrderingType.DatePrev), 25 | new ShowCategorySetting("update",CategoryOrderingType.DatePrev), 26 | new ShowCategorySetting("active",CategoryOrderingType.DateNextPrev), 27 | new ShowCategorySetting("ended",CategoryOrderingType.DatePrev), 28 | new ShowCategorySetting("unknown",CategoryOrderingType.Alphabetical), 29 | new ShowCategorySetting("all",CategoryOrderingType.Alphabetical) 30 | }; 31 | 32 | 33 | 34 | public String Title { get; internal set; } 35 | 36 | public ObservableCollection Shows { get; } = new ObservableCollection(); 37 | public ShowCategorySetting Setting { get; internal set; } 38 | 39 | public void AddShow(ShowTileViewModel show) 40 | { 41 | if (!Shows.Contains(show)) 42 | { 43 | Shows.Add(show); 44 | show.Show.PropertyChanged += Show_PropertyChanged; 45 | Setting.Sort(Shows); 46 | } 47 | } 48 | 49 | public void Sort() 50 | { 51 | Setting.Sort(Shows); 52 | } 53 | 54 | private void Show_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs args) 55 | { 56 | if (args.PropertyName == nameof(FavShowData.NextEpisodeDate) || args.PropertyName == nameof(FavShowData.PreviousEpisodeDate)) 57 | { 58 | _dispatcher.Invoke(delegate 59 | { 60 | Setting.Sort(Shows); 61 | }); 62 | } 63 | } 64 | 65 | 66 | 67 | public void RemoveShow(ShowTileViewModel show) 68 | { 69 | if (Shows.Contains(show)) 70 | { 71 | show.Show.PropertyChanged -= Show_PropertyChanged; 72 | Shows.Remove(show); 73 | } 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /SjUpdater/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | using System.Windows; 4 | 5 | // Allgemeine Informationen über eine Assembly werden über die folgenden 6 | // Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, 7 | // die mit einer Assembly verknüpft sind. 8 | [assembly: AssemblyTitle("SjUpdater")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("SjUpdater")] 13 | [assembly: AssemblyCopyright("Copyright © 2014-2018 Dreamcooled")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar 18 | // für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von 19 | // COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest. 20 | [assembly: ComVisible(false)] 21 | 22 | //Um mit dem Erstellen lokalisierbarer Anwendungen zu beginnen, legen Sie 23 | //ImCodeVerwendeteKultur in der .csproj-Datei 24 | //in einer fest. Wenn Sie in den Quelldateien beispielsweise Deutsch 25 | //(Deutschland) verwenden, legen Sie auf \"de-DE\" fest. Heben Sie dann die Auskommentierung 26 | //des nachstehenden NeutralResourceLanguage-Attributs auf. Aktualisieren Sie "en-US" in der nachstehenden Zeile, 27 | //sodass es mit der UICulture-Einstellung in der Projektdatei übereinstimmt. 28 | 29 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 30 | 31 | 32 | [assembly: ThemeInfo( 33 | ResourceDictionaryLocation.None, //Speicherort der designspezifischen Ressourcenwörterbücher 34 | //(wird verwendet, wenn eine Ressource auf der Seite 35 | // oder in den Anwendungsressourcen-Wörterbüchern nicht gefunden werden kann.) 36 | ResourceDictionaryLocation.SourceAssembly //Speicherort des generischen Ressourcenwörterbuchs 37 | //(wird verwendet, wenn eine Ressource auf der Seite, in der Anwendung oder einem 38 | // designspezifischen Ressourcenwörterbuch nicht gefunden werden kann.) 39 | )] 40 | 41 | 42 | // Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: 43 | // 44 | // Hauptversion 45 | // Nebenversion 46 | // Buildnummer 47 | // Revision 48 | // 49 | // Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern 50 | // übernehmen, indem Sie "*" eingeben: 51 | // [assembly: AssemblyVersion("1.0.*")] 52 | [assembly: AssemblyVersion("0.50.1.0")] 53 | [assembly: AssemblyFileVersion("0.50.1.0")] 54 | [assembly: GuidAttribute("7CE08AB1-B0EE-4D6F-9AB0-28C2F23CB155")] 55 | -------------------------------------------------------------------------------- /SjUpdater/Utils/PropertyChangedImpl.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Diagnostics; 3 | using System.Runtime.CompilerServices; 4 | using System.Windows.Threading; 5 | using SjUpdater.Annotations; 6 | 7 | namespace SjUpdater.Utils 8 | { 9 | public abstract class PropertyChangedImpl : INotifyPropertyChanged 10 | { 11 | #region Constructor 12 | 13 | readonly Dispatcher _dispatcher; 14 | protected PropertyChangedImpl() 15 | { 16 | _dispatcher = Dispatcher.CurrentDispatcher; 17 | } 18 | 19 | #endregion // Constructor 20 | 21 | 22 | #region Debugging Aides 23 | 24 | /// 25 | /// Warns the developer if this object does not have 26 | /// a public property with the specified name. This 27 | /// method does not exist in a Release build. 28 | /// 29 | [Conditional("DEBUG")] 30 | [DebuggerStepThrough] 31 | public void VerifyPropertyName(string propertyName) 32 | { 33 | // Verify that the property name matches a real, 34 | // public, instance property on this object. 35 | if (TypeDescriptor.GetProperties(this)[propertyName] == null) 36 | { 37 | string msg = "Invalid property name: " + propertyName; 38 | Debug.Fail(msg); 39 | } 40 | } 41 | 42 | #endregion // Debugging Aides 43 | 44 | #region INotifyPropertyChanged Members 45 | 46 | /// 47 | /// Raised when a property on this object has a new value. 48 | /// 49 | public event PropertyChangedEventHandler PropertyChanged; 50 | 51 | /// 52 | /// Raises this object's PropertyChanged event. 53 | /// 54 | /// The property that has a new value. 55 | [NotifyPropertyChangedInvocator] 56 | protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "") 57 | { 58 | this.VerifyPropertyName(propertyName); 59 | 60 | PropertyChangedEventHandler handler = this.PropertyChanged; 61 | if (handler == null) return; 62 | PropertyChangedEventArgs e = new PropertyChangedEventArgs(propertyName); 63 | 64 | // if (_dispatcher == null || _dispatcher.CheckAccess()) 65 | handler(this, e); 66 | // else 67 | // _dispatcher.BeginInvoke(new Action(() => handler(this, e))); 68 | } 69 | 70 | protected void OnBigChange() 71 | { 72 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null)); 73 | } 74 | 75 | #endregion // INotifyPropertyChanged Members 76 | 77 | } 78 | } -------------------------------------------------------------------------------- /SjUpdater/Updater/UpdateWindow.xaml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 15 | 16 | 17 | 18 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /SjUpdater/Utils/GlobalMutex.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Runtime.InteropServices; 6 | using System.Security.AccessControl; 7 | using System.Security.Principal; 8 | using System.Text; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | 12 | namespace SjUpdater.Utils 13 | { 14 | public static class GlobalMutex 15 | { 16 | 17 | private static Mutex mutex; 18 | private static bool hasHandle; 19 | 20 | public static bool TryGetMutex() 21 | { 22 | //source: http://stackoverflow.com/questions/229565/what-is-a-good-pattern-for-using-a-global-mutex-in-c 23 | 24 | // get application GUID as defined in AssemblyInfo.cs 25 | //string appGuid = "SjUpdater\\v" +Assembly.GetExecutingAssembly().GetName().Version.Major; 26 | string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), false).GetValue(0)).Value.ToString(); 27 | 28 | // unique id for global mutex - Global prefix means it is global to the machine 29 | string mutexId = string.Format("Global\\{{{0}}}", appGuid); 30 | 31 | mutex = new Mutex(false, mutexId); 32 | 33 | // edited by Jeremy Wiebe to add example of setting up security for multi-user usage 34 | // edited by 'Marc' to work also on localized systems (don't use just "Everyone") 35 | var allowEveryoneRule = new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), 36 | MutexRights.FullControl, AccessControlType.Allow); 37 | var securitySettings = new MutexSecurity(); 38 | securitySettings.AddAccessRule(allowEveryoneRule); 39 | mutex.SetAccessControl(securitySettings); 40 | 41 | // edited by acidzombie24 42 | try 43 | { 44 | try 45 | { 46 | // note, you may want to time out here instead of waiting forever 47 | // edited by acidzombie24 48 | // mutex.WaitOne(Timeout.Infinite, false); 49 | hasHandle = mutex.WaitOne(5000, false); 50 | if (hasHandle == false) 51 | throw new TimeoutException("Timeout waiting for exclusive access"); 52 | } 53 | catch (AbandonedMutexException) 54 | { 55 | // Log the fact the mutex was abandoned in another process, it will still get aquired 56 | hasHandle = true; 57 | } 58 | 59 | return true; 60 | } 61 | catch 62 | { 63 | return false; 64 | } 65 | } 66 | 67 | static public void ReleaseMutex() 68 | { 69 | if (hasHandle) 70 | { 71 | mutex.ReleaseMutex(); 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /SjUpdater/MultiDownloadSelectionHeader.xaml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /SjUpdater/Provider/IProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Xml.Serialization; 7 | 8 | namespace SjUpdater.Provider 9 | { 10 | 11 | public class ShowInformation 12 | { 13 | public String Title { get; set; } 14 | public String Status { get; set; } 15 | public int? NumberEpisodes { get; set; } 16 | public int? NumberSeasons { get; set; } 17 | public String ProviderHomepage { get; set; } 18 | public String PublisherHomepage { get; set; } 19 | 20 | //Only available if withNextPrevEp = true 21 | public DateTime? PreviousEpisodeDate { get; set; } 22 | public DateTime? NextEpisodeDate { get; set; } 23 | public int? PreviousEpisodeSeasonNr { get; set; } 24 | public int? PreviousEpisodeEpisodeNr { get; set; } 25 | public int? NextEpisodeSeasonNr { get; set; } 26 | public int? NextEpisodeEpisodeNr { get; set; } 27 | 28 | //Only available if withImages=true 29 | public object Backdrops { get; set; } 30 | public object Posters { get; set; } 31 | public String Poster { get; set; } 32 | public String Backdrop { get; set; } 33 | } 34 | 35 | public class SeasonInformation 36 | { 37 | public String Title { get; set; } 38 | public String Overview { get; set; } 39 | public DateTime? AirDate { get; set; } 40 | public int? NumberEpisodes { get; set; } 41 | public String ProviderHomepage { get; set; } 42 | public String PublisherHomepage { get; set; } 43 | 44 | //Only available if withImages=true 45 | public object Backdrops { get; set; } 46 | public object Posters { get; set; } 47 | public String Poster { get; set; } 48 | public String Backdrop { get; set; } 49 | } 50 | public class EpisodeInformation 51 | { 52 | public String Title{ get; set; } 53 | [XmlIgnore] //To save storage TODO: remove 54 | public String Overview { get; set; } 55 | public DateTime? AirDate{ get; set; } 56 | public String ProviderHomepage { get; set; } 57 | public String PublisherHomepage { get; set; } 58 | 59 | //Only available if withImages=true 60 | [XmlIgnore] //To save storage. TODO: remove 61 | public object Images { get; set; } 62 | public String Image { get; set; } 63 | 64 | } 65 | 66 | 67 | public interface IProvider 68 | { 69 | object FindShow(String name); 70 | ShowInformation GetShowInformation(object show,bool withImages= true, bool withPreviousNextEp=true); 71 | 72 | SeasonInformation GetSeasonInformation(object show, int season, bool withImages = true); 73 | 74 | EpisodeInformation GetEpisodeInformation(object show, int season, int episode, bool withImages = true); 75 | 76 | String GetFirstImage(object images); 77 | String GetImage(int? maxwidth = null, int? maxheight = null); 78 | } 79 | 80 | 81 | 82 | 83 | } 84 | -------------------------------------------------------------------------------- /SjUpdater/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // Dieser Code wurde von einem Tool generiert. 4 | // Laufzeitversion:4.0.30319.42000 5 | // 6 | // Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn 7 | // der Code erneut generiert wird. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace SjUpdater.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw. 17 | /// 18 | // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert 19 | // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert. 20 | // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen 21 | // mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SjUpdater.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle 51 | /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /SjUpdater/Properties/app.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 54 | -------------------------------------------------------------------------------- /SjUpdater/Utils/SimpleCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using System.Windows.Input; 4 | 5 | namespace SjUpdater.Utils 6 | { 7 | /// 8 | /// Simple delegating command, based largely on DelegateCommand from PRISM/CAL 9 | /// 10 | /// The type for the 11 | public class SimpleCommand : ICommand 12 | { 13 | private Func canExecuteMethod; 14 | private Action executeMethod; 15 | 16 | public SimpleCommand(Func canExecuteMethod, Action executeMethod) 17 | { 18 | this.executeMethod = executeMethod; 19 | this.canExecuteMethod = canExecuteMethod; 20 | } 21 | 22 | public SimpleCommand(Action executeMethod) 23 | { 24 | this.executeMethod = executeMethod; 25 | this.canExecuteMethod = (x) => { return true; }; 26 | } 27 | 28 | public bool CanExecute(T1 parameter) 29 | { 30 | if (canExecuteMethod == null) return true; 31 | return canExecuteMethod(parameter); 32 | } 33 | 34 | public void Execute(T2 parameter) 35 | { 36 | if (executeMethod != null) 37 | { 38 | executeMethod(parameter); 39 | } 40 | } 41 | 42 | public bool CanExecute(object parameter) 43 | { 44 | return CanExecute((T1)parameter); 45 | } 46 | 47 | public void Execute(object parameter) 48 | { 49 | Execute((T2)parameter); 50 | } 51 | 52 | #if SILVERLIGHT 53 | /// 54 | /// Occurs when changes occur that affect whether the command should execute. 55 | /// 56 | public event EventHandler CanExecuteChanged; 57 | #else 58 | /// 59 | /// Occurs when changes occur that affect whether the command should execute. 60 | /// 61 | public event EventHandler CanExecuteChanged 62 | { 63 | add 64 | { 65 | if (canExecuteMethod != null) 66 | { 67 | CommandManager.RequerySuggested += value; 68 | } 69 | } 70 | 71 | remove 72 | { 73 | if (canExecuteMethod != null) 74 | { 75 | CommandManager.RequerySuggested -= value; 76 | } 77 | } 78 | } 79 | #endif 80 | /// 81 | /// Raises the event. 82 | /// 83 | [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", 84 | Justification = "The this keyword is used in the Silverlight version")] 85 | [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate", 86 | Justification = "This cannot be an event")] 87 | public void RaiseCanExecuteChanged() 88 | { 89 | #if SILVERLIGHT 90 | var handler = CanExecuteChanged; 91 | if (handler != null) 92 | { 93 | handler(this, EventArgs.Empty); 94 | } 95 | #else 96 | CommandManager.InvalidateRequerySuggested(); 97 | #endif 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /SjUpdater/ViewModel/MultiSelectionViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Dynamic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows.Input; 8 | using SjUpdater.Model; 9 | using SjUpdater.Utils; 10 | 11 | namespace SjUpdater.ViewModel 12 | { 13 | public class MultiSelectionViewModel : PropertyChangedImpl 14 | { 15 | 16 | 17 | public MultiSelectionViewModel() 18 | { 19 | MarkSelectedAsDownloadedCommand = new SimpleCommand(delegate 20 | { 21 | foreach (var episode in SelectedEpisodes) 22 | { 23 | episode.Downloaded = true; 24 | } 25 | OnPropertyChanged("InfoText2"); 26 | }); 27 | MarkSelectedAsWatchedCommand = new SimpleCommand(delegate 28 | { 29 | foreach (var episode in SelectedEpisodes) 30 | { 31 | episode.Watched = true; 32 | episode.Downloaded = true; 33 | } 34 | OnPropertyChanged("InfoText2"); 35 | }); 36 | UnmarkSelectedCommand= new SimpleCommand(delegate 37 | { 38 | foreach (var episode in SelectedEpisodes) 39 | { 40 | episode.Watched = false; 41 | episode.Downloaded = false; 42 | } 43 | OnPropertyChanged("InfoText2"); 44 | }); 45 | } 46 | 47 | private List _selectedEpisodes = null; 48 | public List SelectedEpisodes 49 | { 50 | get { return _selectedEpisodes;} 51 | set 52 | { 53 | _selectedEpisodes = value; 54 | OnPropertyChanged(); 55 | OnPropertyChanged("InfoText"); 56 | OnPropertyChanged("InfoText2"); 57 | } 58 | } 59 | 60 | public String InfoText 61 | { 62 | get 63 | { 64 | int nrEpisodes = SelectedEpisodes.Count; 65 | 66 | String info = nrEpisodes + " Episodes"; 67 | int seasons = SelectedEpisodes.Select(e => e.Season).Distinct().Count(); 68 | if (seasons == 1) 69 | { 70 | info += " in 1 Season"; 71 | } 72 | else 73 | { 74 | info += " in " + seasons + " Seasons"; 75 | } 76 | return info; 77 | } 78 | } 79 | 80 | public String InfoText2 81 | { 82 | get 83 | { 84 | int nrDownloaded = SelectedEpisodes.Count(e => e.Downloaded); 85 | int nrWatched = SelectedEpisodes.Count(e => e.Watched); 86 | 87 | return nrDownloaded + " Downloaded, " + nrWatched + " Wachted"; 88 | } 89 | } 90 | 91 | public ICommand MarkSelectedAsWatchedCommand { get; private set; } 92 | public ICommand MarkSelectedAsDownloadedCommand { get; private set; } 93 | public ICommand UnmarkSelectedCommand { get; private set; } 94 | 95 | 96 | } 97 | 98 | 99 | 100 | } 101 | -------------------------------------------------------------------------------- /SjUpdater/XML/CustomXmlSerializerBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using System.Xml; 5 | using System.Xml.Serialization; 6 | 7 | namespace SjUpdater.XML 8 | { 9 | public abstract class CustomXmlSerializerBase 10 | { 11 | static Dictionary> propertyInfoCache = new Dictionary>(); 12 | 13 | protected XmlDocument doc = new XmlDocument(); 14 | 15 | protected static IDictionary GetTypePropertyInfo(Type objType) 16 | { 17 | string typeName = objType.FullName; 18 | IDictionary properties; 19 | if (!propertyInfoCache.TryGetValue(typeName, out properties)) 20 | { 21 | // fetch fields 22 | PropertyInfo[] propertyInfos = objType.GetProperties(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance); 23 | 24 | 25 | Dictionary dict = new Dictionary(propertyInfos.Length); 26 | foreach (PropertyInfo property in propertyInfos) 27 | { 28 | if ( !property.PropertyType.IsSubclassOf(typeof(MulticastDelegate))) 29 | { 30 | object[] attribs = property.GetCustomAttributes(typeof(XmlIgnoreAttribute), false); 31 | if (attribs.Length == 0) 32 | { 33 | dict.Add(property.Name, property); 34 | } 35 | } 36 | } 37 | 38 | // check base class as well 39 | Type baseType = objType.BaseType; 40 | if (baseType != null && baseType != typeof(object)) 41 | { 42 | // should we include this base class? 43 | object[] attribs = objType.GetCustomAttributes(typeof(XmlIgnoreBaseTypeAttribute), false); 44 | if (attribs.Length == 0) 45 | { 46 | IDictionary baseProperties = GetTypePropertyInfo(baseType); 47 | // add fields 48 | foreach (KeyValuePair kv in baseProperties) 49 | { 50 | string key = kv.Key; 51 | if (dict.ContainsKey(key)) 52 | { 53 | // make field name unique 54 | key = "base." + key; 55 | } 56 | dict.Add(key, kv.Value); 57 | } 58 | } 59 | } 60 | 61 | properties = dict; 62 | propertyInfoCache.Add(typeName, properties); 63 | } 64 | return properties; 65 | } 66 | 67 | protected class TypeInfo 68 | { 69 | internal int TypeId; 70 | internal XmlElement OnlyElement; 71 | 72 | internal void WriteTypeId(XmlElement element,bool isRuntimeType=false) 73 | { 74 | if(isRuntimeType) 75 | element.SetAttribute("ctypeid", TypeId.ToString()); 76 | else 77 | element.SetAttribute("typeid", TypeId.ToString()); 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /SjUpdater/XML/XmlSerialization.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.IO.Compression; 4 | using System.Xml; 5 | 6 | namespace SjUpdater.XML 7 | { 8 | 9 | 10 | public class XmlSerialization 11 | { 12 | public static Type GetTypeFromXml(string filename) 13 | { 14 | return CustomXmlDeserializer.GetTypeOfContent(XmlFileReader.ReadXmlFile(filename).OuterXml); 15 | } 16 | public static T LoadFromXml(string filename, int maxversion, out int actualversion) 17 | { 18 | try 19 | { 20 | // load XML document and parse it 21 | // deserialize a Test1 instance having a version number of at most maxversion 22 | T obj = (T)CustomXmlDeserializer.Deserialize(XmlFileReader.ReadXmlFile(filename).OuterXml, maxversion,out actualversion); 23 | return obj; 24 | } 25 | catch (Exception ex ) 26 | { 27 | throw new Exception("Fehler beim Lesen von Xml Datei",ex); 28 | } 29 | } 30 | public static void SaveToXml(object o, string filename, int version, bool encrypt = false) 31 | { 32 | try 33 | { 34 | XmlDocument doc = CustomXmlSerializer.Serialize(o, version, "AMS_FILE"); 35 | 36 | if (!encrypt) 37 | doc.Save(filename); 38 | else 39 | { 40 | FileStream fileStream = new FileStream(filename, FileMode.Create); 41 | fileStream.Write(XmlFileReader.EncHeader, 0, 10); 42 | 43 | GZipStream compressStream = new GZipStream(fileStream, CompressionMode.Compress); 44 | 45 | doc.Save(compressStream); 46 | 47 | compressStream.Close(); 48 | fileStream.Close(); 49 | } 50 | 51 | } 52 | catch (Exception ex) 53 | { 54 | throw new Exception("Fehler beim Schreiben zu Xml Datei",ex); 55 | } 56 | } 57 | } 58 | 59 | 60 | class XmlFileReader 61 | { 62 | static public byte[] EncHeader = new byte[] { 0xb1, 0x83, 0xc2, 0x06, 0x9f, 0x50, 0x46, 0xd1, 0x17, 0xd9 }; 63 | //random byte genrator^^ 64 | static public XmlDocument ReadXmlFile(string Filepath) 65 | { 66 | Stream inputstream = new FileStream(Filepath, FileMode.Open); 67 | 68 | byte[] data= new byte[10]; 69 | inputstream.Read(data, 0, 10); 70 | 71 | bool isEncrypted = true; 72 | for (int i = 0; i < 10; i++) 73 | { 74 | if (data[i] != EncHeader[i]) 75 | { 76 | isEncrypted = false; 77 | break; 78 | } 79 | } 80 | 81 | 82 | Stream outputstream; 83 | if (isEncrypted) 84 | outputstream = new GZipStream(inputstream, CompressionMode.Decompress); //We take the decompressed stream. 85 | else 86 | { 87 | inputstream.Seek(0, SeekOrigin.Begin); 88 | outputstream = inputstream; // We can just take the input stream 89 | } 90 | 91 | XmlDocument xmlDoc = new XmlDocument(); 92 | xmlDoc.Load(outputstream); 93 | 94 | outputstream.Close(); 95 | 96 | return xmlDoc; 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /SjUpdater/Utils/GridViewColumnVisibilityManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Controls; 9 | using System.Windows.Media; 10 | using MahApps.Metro.Controls; 11 | 12 | namespace SjUpdater.Utils 13 | { 14 | 15 | //Copied from http://stackoverflow.com/questions/20985005/get-parent-for-gridviewcolumn 16 | public static class DependencyObjectExtensions 17 | { 18 | private static readonly PropertyInfo InheritanceContextProp = typeof(DependencyObject).GetProperty("InheritanceContext", BindingFlags.NonPublic | BindingFlags.Instance); 19 | 20 | public static IEnumerable GetParents(this DependencyObject child) 21 | { 22 | while (child != null) 23 | { 24 | var parent = LogicalTreeHelper.GetParent(child); 25 | if (parent == null) 26 | { 27 | if (child is FrameworkElement) 28 | { 29 | parent = VisualTreeHelper.GetParent(child); 30 | } 31 | if (parent == null && child is ContentElement) 32 | { 33 | parent = ContentOperations.GetParent((ContentElement)child); 34 | } 35 | if (parent == null) 36 | { 37 | parent = InheritanceContextProp.GetValue(child, null) as DependencyObject; 38 | } 39 | } 40 | child = parent; 41 | yield return parent; 42 | } 43 | } 44 | } 45 | 46 | 47 | 48 | 49 | //Adapted from http://stackoverflow.com/a/9634769/2606757 50 | public class GridViewColumnVisibilityManager 51 | { 52 | static void UpdateListView(ListView lv) 53 | { 54 | GridView gridview = lv.View as GridView; 55 | if (gridview == null || gridview.Columns == null) return; 56 | List toRemove = new List(); 57 | foreach (GridViewColumn gc in gridview.Columns) 58 | { 59 | if (GetIsVisible(gc) == false) 60 | { 61 | toRemove.Add(gc); 62 | } 63 | } 64 | foreach (GridViewColumn gc in toRemove) 65 | { 66 | gridview.Columns.Remove(gc); 67 | } 68 | } 69 | 70 | public static bool GetIsVisible(DependencyObject obj) 71 | { 72 | return (bool)obj.GetValue(IsVisibleProperty); 73 | } 74 | 75 | public static void SetIsVisible(DependencyObject obj, bool value) 76 | { 77 | obj.SetValue(IsVisibleProperty, value); 78 | } 79 | 80 | public static readonly DependencyProperty IsVisibleProperty = 81 | DependencyProperty.RegisterAttached("IsVisible", typeof(bool), typeof(GridViewColumnVisibilityManager), new UIPropertyMetadata(true,new PropertyChangedCallback(OnVisibleChanged))); 82 | 83 | private static void OnVisibleChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) 84 | { 85 | var parents = dependencyObject.GetParents(); 86 | foreach (DependencyObject parent in parents) 87 | { 88 | var l = parent as ListView; 89 | if (l != null) 90 | { 91 | UpdateListView(l); 92 | return; 93 | } 94 | } 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /SjUpdater/Model/FavEpisodeData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.ObjectModel; 3 | using System.Xml.Serialization; 4 | using SjUpdater.Provider; 5 | using SjUpdater.Utils; 6 | 7 | namespace SjUpdater.Model 8 | { 9 | public class FavEpisodeData : PropertyChangedImpl 10 | { 11 | private int _number; 12 | private bool _newEpisode; 13 | private bool _newUpdate; 14 | private bool _watched; 15 | private bool _downloaded; 16 | private ObservableCollection _downloads; 17 | private FavSeasonData _season; 18 | private EpisodeInformation _episodeInformation; 19 | 20 | public FavEpisodeData() 21 | { 22 | _downloads = new ObservableCollection(); 23 | _number = -1; 24 | _episodeInformation = null; 25 | _newEpisode = false; 26 | _newUpdate = false; 27 | _downloaded = false; 28 | _watched = false; 29 | } 30 | 31 | public EpisodeInformation EpisodeInformation 32 | { 33 | get { return _episodeInformation; } 34 | set 35 | { 36 | _episodeInformation = value; 37 | OnPropertyChanged(); 38 | } 39 | } 40 | 41 | public FavSeasonData Season 42 | { 43 | get { return _season; } 44 | set 45 | { 46 | _season = value; 47 | OnPropertyChanged(); 48 | } 49 | } 50 | public int Number 51 | { 52 | get { return _number; } 53 | set 54 | { 55 | _number = value; 56 | OnPropertyChanged(); 57 | } 58 | } 59 | 60 | public bool Watched 61 | { 62 | get { return _watched; } 63 | set 64 | { 65 | if (value == _watched) 66 | return; 67 | _watched = value; 68 | OnPropertyChanged(); 69 | } 70 | } 71 | public bool Downloaded 72 | { 73 | get { return _downloaded; } 74 | set 75 | { 76 | if (value == _downloaded) 77 | return; 78 | _downloaded = value; 79 | OnPropertyChanged(); 80 | } 81 | } 82 | 83 | /// 84 | /// Is set to true when the episode is new. Reset this to false, yourself 85 | /// 86 | public bool NewEpisode 87 | { 88 | get { return _newEpisode; } 89 | internal set 90 | { 91 | if (value == _newEpisode) return; 92 | _newEpisode = value; 93 | OnPropertyChanged(); 94 | } 95 | } 96 | 97 | /// 98 | /// Is set to true when the episode received a new Download. Reset this to false, yourself 99 | /// 100 | public bool NewUpdate 101 | { 102 | get { return _newUpdate; } 103 | internal set 104 | { 105 | if (value == _newUpdate || _newEpisode) return; 106 | _newUpdate = value; 107 | OnPropertyChanged(); 108 | } 109 | } 110 | 111 | public ObservableCollection Downloads 112 | { 113 | get { return _downloads; } 114 | internal set 115 | { 116 | _downloads = value; 117 | OnPropertyChanged(); 118 | } 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /SjUpdater/Utils/EnumText.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Reflection; 5 | using System.Windows.Data; 6 | 7 | namespace SjUpdater.Utils 8 | { 9 | [AttributeUsage(AttributeTargets.Field)] 10 | public class EnumText : Attribute 11 | { 12 | public static string[] GetStringValues(Type EnumType) 13 | { 14 | if (!EnumType.IsEnum) 15 | throw new Exception("This type is not an enumeration!"); 16 | 17 | List results = new List(); 18 | 19 | foreach (FieldInfo f in EnumType.GetFields()) 20 | { 21 | object[] attributes = f.GetCustomAttributes(typeof(EnumText), true); 22 | 23 | if (attributes.Length > 0) 24 | { 25 | results.Add(((EnumText)attributes[0]).String); 26 | } 27 | } 28 | 29 | return results.ToArray(); 30 | } 31 | 32 | public static string GetStringValue(Enum Value) 33 | { 34 | Type enumType = Value.GetType(); 35 | 36 | foreach (FieldInfo f in enumType.GetFields()) 37 | { 38 | if (!f.IsStatic) 39 | continue; 40 | 41 | object[] attributes = f.GetCustomAttributes(typeof(EnumText), true); 42 | 43 | if (Object.Equals(Value, f.GetValue(f))) 44 | { 45 | if (attributes.Length > 0) 46 | { 47 | return ((EnumText)attributes[0]).String; 48 | } 49 | else 50 | { 51 | break; 52 | } 53 | } 54 | } 55 | 56 | throw new Exception("No StringValue found for this enumeration-value!"); 57 | } 58 | 59 | public static Enum GetEnumValue(string StringValue, Type EnumType) 60 | { 61 | if (!EnumType.IsEnum) 62 | throw new Exception("This type is not an enumeration!"); 63 | 64 | foreach (FieldInfo f in EnumType.GetFields()) 65 | { 66 | object[] attributes = f.GetCustomAttributes(typeof(EnumText), true); 67 | 68 | if (attributes.Length > 0) 69 | { 70 | if (((EnumText)attributes[0]).String == StringValue) 71 | { 72 | return (Enum)f.GetValue(f); 73 | } 74 | } 75 | } 76 | 77 | throw new Exception("StringValue not found!"); 78 | } 79 | 80 | public EnumText(string StringValue) 81 | { 82 | String = StringValue; 83 | } 84 | 85 | string String { get; set; } 86 | 87 | 88 | public override string ToString() 89 | { 90 | return String; 91 | } 92 | } 93 | 94 | public class EnumTextValueConverter : IValueConverter 95 | { 96 | public object Convert(object value, Type targetType, 97 | object parameter, CultureInfo culture) 98 | { 99 | if (targetType != typeof(String) || !value.GetType().IsEnum) 100 | { 101 | throw new ArgumentException(); 102 | } 103 | return EnumText.GetStringValue(value as Enum); 104 | } 105 | 106 | public object ConvertBack(object value, Type targetType, 107 | object parameter, CultureInfo culture) 108 | { 109 | if (!targetType.IsEnum || value.GetType() != typeof(String)) 110 | { 111 | throw new ArgumentException(); 112 | } 113 | return EnumText.GetEnumValue(value as String, targetType); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /SjUpdater/Utils/Crc64.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Security.Cryptography; 4 | 5 | namespace SjUpdater.Utils 6 | { 7 | /// 8 | /// Implements a 64-bit CRC hash algorithm for a given polynomial. 9 | /// 10 | /// 11 | /// For ISO 3309 compliant 64-bit CRC's use Crc64Iso. 12 | /// 13 | public class Crc64 : HashAlgorithm 14 | { 15 | public const ulong DefaultSeed = 0x0; 16 | 17 | private readonly ulong[] _table; 18 | 19 | private readonly ulong _seed; 20 | private ulong _hash; 21 | 22 | public Crc64(ulong polynomial) : this(polynomial, DefaultSeed) 23 | { 24 | } 25 | 26 | public Crc64(ulong polynomial, ulong seed) 27 | { 28 | _table = InitializeTable(polynomial); 29 | _seed = _hash = seed; 30 | } 31 | 32 | public override void Initialize() 33 | { 34 | _hash = _seed; 35 | } 36 | 37 | protected override void HashCore(byte[] buffer, int start, int length) 38 | { 39 | _hash = CalculateHash(_hash, _table, buffer, start, length); 40 | } 41 | 42 | protected override byte[] HashFinal() 43 | { 44 | var hashBuffer = UInt64ToBigEndianBytes(_hash); 45 | HashValue = hashBuffer; 46 | return hashBuffer; 47 | } 48 | 49 | public override int HashSize => 64; 50 | 51 | protected static ulong CalculateHash(ulong seed, ulong[] table, IList buffer, int start, int size) 52 | { 53 | var crc = seed; 54 | 55 | for (var i = start; i < size; i++) 56 | unchecked 57 | { 58 | crc = (crc >> 8) ^ table[(buffer[i] ^ crc) & 0xff]; 59 | } 60 | 61 | return crc; 62 | } 63 | 64 | private static byte[] UInt64ToBigEndianBytes(ulong value) 65 | { 66 | var result = BitConverter.GetBytes(value); 67 | 68 | if (BitConverter.IsLittleEndian) 69 | Array.Reverse(result); 70 | 71 | return result; 72 | } 73 | 74 | private static ulong[] InitializeTable(ulong polynomial) 75 | { 76 | if (polynomial == Crc64Iso.Iso3309Polynomial && Crc64Iso.Table != null) 77 | return Crc64Iso.Table; 78 | 79 | var createTable = CreateTable(polynomial); 80 | 81 | if (polynomial == Crc64Iso.Iso3309Polynomial) 82 | Crc64Iso.Table = createTable; 83 | 84 | return createTable; 85 | } 86 | 87 | protected static ulong[] CreateTable(ulong polynomial) 88 | { 89 | var createTable = new ulong[256]; 90 | for (var i = 0; i < 256; ++i) 91 | { 92 | var entry = (ulong)i; 93 | for (var j = 0; j < 8; ++j) 94 | if ((entry & 1) == 1) 95 | entry = (entry >> 1) ^ polynomial; 96 | else 97 | entry = entry >> 1; 98 | createTable[i] = entry; 99 | } 100 | return createTable; 101 | } 102 | } 103 | 104 | public class Crc64Iso : Crc64 105 | { 106 | internal static ulong[] Table; 107 | 108 | public const ulong Iso3309Polynomial = 0xD800000000000000; 109 | 110 | public Crc64Iso() : base(Iso3309Polynomial) 111 | { 112 | } 113 | 114 | public Crc64Iso(ulong seed) : base(Iso3309Polynomial, seed) 115 | { 116 | } 117 | 118 | public static ulong Compute(byte[] buffer) 119 | { 120 | return Compute(DefaultSeed, buffer); 121 | } 122 | 123 | public static ulong Compute(ulong seed, byte[] buffer) 124 | { 125 | if (Table == null) 126 | Table = CreateTable(Iso3309Polynomial); 127 | 128 | return CalculateHash(seed, Table, buffer, 0, buffer.Length); 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /SjUpdater/SpecialDownloadList.xaml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 57 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /SjUpdater/MultiDownloadSelectionGrid.xaml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 15 | 29 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 59 | 60 | 76 | 77 | 78 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 33 | 34 | 35 | 37 | 45 | 46 | 55 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /SjUpdater/Model/ShowCategorySetting.cs: -------------------------------------------------------------------------------- 1 | using SjUpdater.ViewModel; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Collections.ObjectModel; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using SjUpdater.Utils; 9 | 10 | namespace SjUpdater.Model 11 | { 12 | public enum CategoryOrderingType 13 | { 14 | [EnumText("Alphabetical")] 15 | Alphabetical, 16 | [EnumText("Date (Next+Previous)")] 17 | DateNextPrev, 18 | [EnumText("Date (Next)")] 19 | DateNext, 20 | [EnumText("Date (Previous)")] 21 | DatePrev, 22 | [EnumText("Date (Previous+Next)")] 23 | DatePrevNext 24 | } 25 | 26 | public class ShowCategorySetting : PropertyChangedImpl 27 | { 28 | 29 | private static readonly Comparer AlphabeticalShowComparer = Comparer.Create((m1, m2) => String.CompareOrdinal(m1.Title.ToLower(), m2.Title.ToLower())); 30 | 31 | private static readonly Comparer DateNextShowComparer = Comparer.Create( 32 | (vm1, vm2) => 33 | { 34 | if (vm1.Show.NextEpisodeDate.HasValue && vm2.Show.NextEpisodeDate.HasValue) //both have next ep date 35 | { 36 | if (vm1.Show.NextEpisodeDate.Value.Date != vm2.Show.NextEpisodeDate.Value.Date) 37 | { 38 | if (vm1.Show.NextEpisodeDate.Value.Date < vm2.Show.NextEpisodeDate.Value.Date) 39 | { 40 | return -1; 41 | } 42 | else 43 | { 44 | return 1; 45 | } 46 | } 47 | } 48 | else if (vm1.Show.NextEpisodeDate.HasValue) 49 | { 50 | return -1; 51 | } 52 | else if (vm2.Show.NextEpisodeDate.HasValue) 53 | { 54 | return 1; 55 | } 56 | return 0; 57 | }); 58 | 59 | private static readonly Comparer DateNextAlphaShowComparer = 60 | ExtensionMethods.CreateMultiple(DateNextShowComparer, AlphabeticalShowComparer); 61 | 62 | private static readonly Comparer DatePrevShowComparer = Comparer.Create( 63 | (vm1, vm2) => 64 | { 65 | if (vm1.Show.PreviousEpisodeDate.HasValue && vm2.Show.PreviousEpisodeDate.HasValue) //both have prev ep date 66 | { 67 | if (vm1.Show.PreviousEpisodeDate.Value.Date != vm2.Show.PreviousEpisodeDate.Value.Date) 68 | { 69 | if (vm1.Show.PreviousEpisodeDate.Value.Date > vm2.Show.PreviousEpisodeDate.Value.Date) 70 | { 71 | return -1; 72 | } 73 | else 74 | { 75 | return 1; 76 | } 77 | } 78 | 79 | } 80 | else if (vm1.Show.PreviousEpisodeDate.HasValue) 81 | { 82 | return -1; 83 | } 84 | else if (vm2.Show.PreviousEpisodeDate.HasValue) 85 | { 86 | return 1; 87 | } 88 | return 0; 89 | }); 90 | private static readonly Comparer DatePrevAlphaShowComparer = 91 | ExtensionMethods.CreateMultiple(DatePrevShowComparer, AlphabeticalShowComparer); 92 | 93 | private static readonly Comparer DateNextPrevAlphaShowComparer = 94 | ExtensionMethods.CreateMultiple(DateNextShowComparer,DatePrevShowComparer, AlphabeticalShowComparer); 95 | private static readonly Comparer DatePrevNextAlphaShowComparer = 96 | ExtensionMethods.CreateMultiple(DatePrevShowComparer, DateNextShowComparer, AlphabeticalShowComparer); 97 | 98 | 99 | private static readonly Dictionary> Comparers = new Dictionary>() 100 | { 101 | {CategoryOrderingType.Alphabetical, AlphabeticalShowComparer }, 102 | {CategoryOrderingType.DateNext, DateNextAlphaShowComparer }, 103 | {CategoryOrderingType.DatePrev, DatePrevAlphaShowComparer }, 104 | {CategoryOrderingType.DateNextPrev, DateNextPrevAlphaShowComparer }, 105 | {CategoryOrderingType.DatePrevNext, DatePrevNextAlphaShowComparer } 106 | }; 107 | 108 | private bool _enabled; 109 | private CategoryOrderingType _orderingType; 110 | private string _title; 111 | private string _description; 112 | 113 | private static readonly Dictionary CategoryDescriptions = new Dictionary() 114 | { 115 | {"new","Shows with new Episodes"}, 116 | {"update", "Shows with updated Episodes" }, 117 | {"active","Active Shows"}, 118 | {"ended","Shows that have ended"}, 119 | {"unknown","Shows with unknown state" }, 120 | {"all","All your Tv Shows"} 121 | }; 122 | 123 | 124 | public ShowCategorySetting(String title = null, CategoryOrderingType ordering=CategoryOrderingType.Alphabetical, bool enabled=true) 125 | { 126 | Title = title; 127 | OrderingType = ordering; 128 | Enabled = enabled; 129 | } 130 | 131 | public ShowCategorySetting() 132 | { 133 | Enabled = true; 134 | } 135 | 136 | 137 | public String Title 138 | { 139 | get { return _title; } 140 | internal set 141 | { 142 | if (value == _title) return; 143 | _title = value; 144 | if (_description == null && CategoryDescriptions.ContainsKey(value)) 145 | { 146 | Description = CategoryDescriptions[value]; 147 | } 148 | OnPropertyChanged(); 149 | } 150 | } 151 | 152 | public String Description 153 | { 154 | get { return _description; } 155 | internal set 156 | { 157 | if (value == _description) return; 158 | _description = value; 159 | OnPropertyChanged(); 160 | } 161 | } 162 | 163 | 164 | public CategoryOrderingType OrderingType 165 | { 166 | get { return _orderingType; } 167 | set 168 | { 169 | if (value == _orderingType) return; 170 | _orderingType = value; 171 | OnPropertyChanged(); 172 | } 173 | } 174 | 175 | public bool Enabled 176 | { 177 | get { return _enabled; } 178 | set 179 | { 180 | if (value == _enabled) return; 181 | _enabled = value; 182 | OnPropertyChanged(); 183 | } 184 | } 185 | 186 | public void Sort(ObservableCollection shows) 187 | { 188 | shows.Sort(Comparers[OrderingType]); 189 | } 190 | 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /SjUpdater/DownloadPopupList.xaml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /SjUpdater/Utils/FavIcon.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Drawing.Drawing2D; 5 | using System.Drawing.Imaging; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Net; 9 | using System.Text.RegularExpressions; 10 | using System.Windows.Media; 11 | using System.Windows.Media.Imaging; 12 | using Amib.Threading; 13 | 14 | namespace SjUpdater.Utils 15 | { 16 | public class FavIcon : PropertyChangedImpl 17 | { 18 | 19 | private static Dictionary _dictCache = new Dictionary(); 20 | private static readonly string cachePath = Path.Combine(Path.GetTempPath(), "sjupdater", "faviconcache"); 21 | private ImageSource _image; 22 | private string _name; 23 | private static object lockobj = new object(); 24 | 25 | static FavIcon() 26 | { 27 | Directory.CreateDirectory(cachePath); 28 | var files = Directory.GetFiles(cachePath); 29 | foreach (var file in files) 30 | { 31 | var fs = new FileStream(file, FileMode.Open, FileAccess.Read); 32 | var bitmap = BitmapImageFromStream(fs); 33 | _dictCache.Add(Path.GetFileNameWithoutExtension(file), bitmap); 34 | fs.Close(); 35 | } 36 | } 37 | 38 | public FavIcon(string name) 39 | { 40 | Name = name; 41 | } 42 | 43 | public string Name 44 | { 45 | get { return _name; } 46 | set 47 | { 48 | if (_name == value) return; 49 | _name = value; 50 | 51 | var b = GetFromCache(value); 52 | if (b == null) b = GetFromLetters(value); 53 | Image = b; 54 | 55 | StaticInstance.ThreadPool.QueueWorkItem(() => 56 | { 57 | lock(lockobj) 58 | { 59 | var x = Get(value); 60 | if (x != null) 61 | { 62 | Image = x; 63 | } 64 | } 65 | }, true, WorkItemPriority.AboveNormal); 66 | OnPropertyChanged(); 67 | } 68 | } 69 | 70 | public ImageSource Image 71 | { 72 | get { return _image; } 73 | set 74 | { 75 | if (_image == value) return; 76 | _image = value; 77 | OnPropertyChanged(); 78 | } 79 | } 80 | 81 | private static BitmapImage Get(string value) 82 | { 83 | try 84 | { 85 | return GetFromCache(value) ?? GetFromUrl(value); 86 | } 87 | catch (Exception) 88 | { 89 | return null; 90 | } 91 | } 92 | 93 | private static BitmapImage GetFromLetters(string value) 94 | { 95 | var bmp = new Bitmap(48, 48); 96 | var rectf = new RectangleF(0, 0, 48, 48); 97 | var g = Graphics.FromImage(bmp); 98 | //g.Clear(System.Drawing.Color.Gray); 99 | 100 | g.SmoothingMode = SmoothingMode.AntiAlias; 101 | g.InterpolationMode = InterpolationMode.HighQualityBicubic; 102 | g.PixelOffsetMode = PixelOffsetMode.HighQuality; 103 | var format = new StringFormat(); 104 | format.LineAlignment = StringAlignment.Center; 105 | format.Alignment = StringAlignment.Center; 106 | var c = ((SolidColorBrush)App.Current.FindResource("LabelTextBrush")).Color; // ye i know, it's hacky but it works 107 | g.DrawString("" + value.ToUpper().First(), new Font("Tahoma", 40, FontStyle.Bold, GraphicsUnit.Pixel), new SolidBrush(System.Drawing.Color.FromArgb(c.A, c.R, c.G, c.B)), rectf, format); 108 | g.Flush(); 109 | var ms = new MemoryStream(); 110 | bmp.Save(ms, ImageFormat.Png); 111 | ms.Position = 0; 112 | return BitmapImageFromStream(ms); 113 | } 114 | 115 | private static BitmapImage GetFromCache(string value) 116 | { 117 | value.ToLower(); 118 | foreach (var key in _dictCache.Keys) 119 | { 120 | var key2 = key.ToLower(); 121 | if (key2.Contains(value) || value.Contains(key2)) 122 | { 123 | return _dictCache[key]; 124 | } 125 | } 126 | return null; 127 | } 128 | 129 | private static string FindUrl(string value) 130 | { 131 | if (!value.StartsWith("http")) 132 | { 133 | value = "http://" + value; 134 | } 135 | var u = new Uri(value); 136 | if (Uri.CheckHostName(u.DnsSafeHost) == UriHostNameType.Unknown) 137 | { 138 | return null; 139 | } 140 | 141 | var req = HttpWebRequest.CreateHttp(value); 142 | req.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64)"; 143 | req.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; 144 | req.AllowAutoRedirect = true; 145 | var res = req.GetResponse(); 146 | var reader = new StreamReader(res.GetResponseStream()); 147 | var html = reader.ReadToEnd(); 148 | var mts = new Regex("]*", RegexOptions.IgnoreCase).Matches(html); 149 | foreach (Match mt in mts) 150 | { 151 | var m = mt.Value.ToLower(); 152 | if (new Regex("rel\\s*=\\s*['\"][a-z0-9_\\- ]*(icon|shortcut)[a-z0-9_\\- ]*['\"]", RegexOptions.IgnoreCase).Match(m).Success) 153 | { 154 | var murl = new Regex("href\\s*=\\s*['\"]([^'\"]+)['\"]", RegexOptions.IgnoreCase).Match(m); 155 | if (murl.Success) 156 | { 157 | var path = murl.Groups[1].Value; 158 | if (!path.StartsWith("http")) 159 | { 160 | path = res.ResponseUri + "/" + path; 161 | } 162 | return path; 163 | } 164 | } 165 | } 166 | return null; 167 | } 168 | 169 | private static BitmapImage GetFromUrl(string value) 170 | { 171 | var url = FindUrl(value); 172 | if (url == null) return null; 173 | var url_parts = new Uri(url).DnsSafeHost.Split(new char[] {'.'}); 174 | var key = url_parts[url_parts.Length - 2]; 175 | 176 | var ms = new MemoryStream(); 177 | var request = WebRequest.CreateHttp(url); 178 | 179 | var response = request.GetResponse() as HttpWebResponse; 180 | var responseStream = response.GetResponseStream(); 181 | responseStream.CopyTo(ms); 182 | responseStream.Close(); 183 | response.Close(); 184 | 185 | ms.Position = 0; 186 | var f = new FileStream(Path.Combine(cachePath, key + url.Substring(url.LastIndexOf('.'))), FileMode.Create, FileAccess.Write); 187 | ms.CopyTo(f); 188 | 189 | f.Close(); 190 | ms.Position = 0; 191 | var bmap = BitmapImageFromStream(ms); 192 | _dictCache.Add(key, bmap); 193 | return bmap; 194 | } 195 | 196 | public static BitmapImage BitmapImageFromStream(Stream stream, bool freeze = true) 197 | { 198 | try 199 | { 200 | var image = new BitmapImage(); 201 | image.BeginInit(); 202 | image.CacheOption = BitmapCacheOption.OnLoad; 203 | image.StreamSource = stream; 204 | image.EndInit(); 205 | if (freeze) 206 | image.Freeze(); 207 | 208 | return image; 209 | } 210 | catch (Exception) 211 | { 212 | // ignored 213 | } 214 | return null; 215 | } 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /SjUpdater/Provider/TMDb.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text.RegularExpressions; 5 | using TMDbLib.Client; 6 | using TMDbLib.Objects.General; 7 | using TMDbLib.Objects.TvShows; 8 | 9 | namespace SjUpdater.Provider 10 | { 11 | public class TMDb : IProvider 12 | { 13 | private readonly TMDbClient client; 14 | private const string API_KEY = "32b427eaba430fb1c86e9d15049e7799"; 15 | public TMDb() 16 | { 17 | try { 18 | client = new TMDbClient(API_KEY); 19 | client.GetConfig(); 20 | //client.MaxRetryCount = 15; 21 | } catch(Exception ex) { 22 | throw ex; 23 | } 24 | 25 | } 26 | 27 | public object FindShow(String name) 28 | { 29 | if (!client.HasConfig || client?.Config == null) return null; 30 | Regex r = new Regex(@"\(([12]\d{3})\)"); //name contains year? 31 | Match m = r.Match(name); 32 | if (!m.Success) 33 | { 34 | var x = client.SearchTvShow(name); 35 | if (x.TotalResults > 0) 36 | { 37 | return x.Results.First().Id; 38 | } 39 | return null; 40 | } 41 | else 42 | { 43 | int year = int.Parse(m.Groups[1].Value); 44 | name=name.Replace(m.Value, "").Trim();//remove year 45 | var x = client.SearchTvShow(name); 46 | foreach (var result in x.Results) 47 | { 48 | if (result.FirstAirDate.HasValue && result.FirstAirDate.Value.Year == year) 49 | { 50 | return result.Id; 51 | } 52 | } 53 | 54 | return null; 55 | } 56 | } 57 | 58 | public ShowInformation GetShowInformation(object show, bool withImages, bool withPreviousNextEp) 59 | { 60 | if(!(show is int)) throw new ArgumentException(); 61 | if (!client.HasConfig || client?.Config == null) return null; 62 | var showinfo = client.GetTvShow((int) show,withImages?TvShowMethods.Images:TvShowMethods.Undefined); 63 | if(showinfo?.Name == null) return null; 64 | 65 | var si= new ShowInformation 66 | { 67 | Status = showinfo.Status, 68 | PreviousEpisodeDate = showinfo.LastAirDate, 69 | NumberEpisodes = showinfo.NumberOfEpisodes, 70 | NumberSeasons = showinfo.NumberOfSeasons, 71 | ProviderHomepage = "https://www.themoviedb.org/tv/"+((int)show), 72 | PublisherHomepage = (showinfo.Homepage==null || showinfo.Homepage.Trim().Length==0) ?null:showinfo.Homepage, 73 | Title = showinfo.OriginalName, 74 | Backdrop = String.IsNullOrWhiteSpace(showinfo.BackdropPath) ? null : client.GetImageUrl("original", showinfo.BackdropPath).AbsoluteUri, 75 | Poster = String.IsNullOrWhiteSpace(showinfo.PosterPath) ? null : client.GetImageUrl("original", showinfo.PosterPath).AbsoluteUri, 76 | Backdrops = showinfo.Images?.Backdrops, 77 | Posters = showinfo.Images?.Posters 78 | }; 79 | 80 | if (withPreviousNextEp) 81 | { 82 | 83 | bool first = true; 84 | int lastSeasonNr = -1; 85 | int lastEpisodeNr = -1; 86 | DateTime lastEpisodeDateTime = DateTime.MinValue; 87 | 88 | for (int curSeasonIdx = showinfo.Seasons.Count - 1; curSeasonIdx >= 0; curSeasonIdx--) 89 | { 90 | var seasoninfo = client.GetTvSeason((int) show, showinfo.Seasons[curSeasonIdx].SeasonNumber); 91 | if (seasoninfo == null) return si; 92 | 93 | for (int curEpisodeIdx = seasoninfo.Episodes.Count - 1; curEpisodeIdx >= 0; curEpisodeIdx--) 94 | { 95 | if (seasoninfo.Episodes[curEpisodeIdx].AirDate.Date <= DateTime.Today) 96 | //air date of ep lies in past 97 | { 98 | if (first) 99 | //first check => there's no next episode because the first that we checked already lies in the past 100 | { 101 | si.PreviousEpisodeSeasonNr = showinfo.Seasons[curSeasonIdx].SeasonNumber; 102 | si.PreviousEpisodeEpisodeNr = seasoninfo.Episodes[curEpisodeIdx].EpisodeNumber; 103 | si.PreviousEpisodeDate = seasoninfo.Episodes[curEpisodeIdx].AirDate; 104 | return si; 105 | } 106 | else 107 | // no the first check => the last ep we iterated through must be the "next" to be aired 108 | { 109 | si.NextEpisodeSeasonNr = lastSeasonNr; 110 | si.NextEpisodeEpisodeNr = lastEpisodeNr; 111 | si.NextEpisodeDate = lastEpisodeDateTime; 112 | si.PreviousEpisodeSeasonNr = showinfo.Seasons[curSeasonIdx].SeasonNumber; 113 | si.PreviousEpisodeEpisodeNr = seasoninfo.Episodes[curEpisodeIdx].EpisodeNumber; 114 | si.PreviousEpisodeDate = seasoninfo.Episodes[curEpisodeIdx].AirDate; 115 | return si; 116 | } 117 | } 118 | first = false; 119 | lastEpisodeNr = seasoninfo.Episodes[curEpisodeIdx].EpisodeNumber; 120 | lastEpisodeDateTime = seasoninfo.Episodes[curEpisodeIdx].AirDate; 121 | lastSeasonNr = showinfo.Seasons[curSeasonIdx].SeasonNumber; 122 | } 123 | } 124 | } 125 | return si; 126 | } 127 | 128 | public SeasonInformation GetSeasonInformation(object show, int season, bool withImages) 129 | { 130 | if (!(show is int)) throw new ArgumentException(); 131 | if (!client.HasConfig || client?.Config == null) return null; 132 | var seasoninfo = client.GetTvSeason((int)show,season, withImages ? TvSeasonMethods.Images : TvSeasonMethods.Undefined); 133 | if (seasoninfo?.Name == null) return null; 134 | 135 | return new SeasonInformation 136 | { 137 | AirDate = seasoninfo.AirDate, 138 | Overview = seasoninfo.Overview, 139 | Title = seasoninfo.Name, 140 | NumberEpisodes = seasoninfo.Episodes.Count, 141 | ProviderHomepage = "https://www.themoviedb.org/tv/" + ((int)show) + "/season/"+season, 142 | PublisherHomepage = null, 143 | Backdrop = null, 144 | Poster = String.IsNullOrWhiteSpace(seasoninfo.PosterPath) ? null : client.GetImageUrl("original", seasoninfo.PosterPath).AbsoluteUri, 145 | Backdrops = seasoninfo.Images?.Backdrops, 146 | Posters = seasoninfo.Images?.Posters 147 | }; 148 | } 149 | 150 | public EpisodeInformation GetEpisodeInformation(object show, int season, int episode, bool withImages) 151 | { 152 | if (!(show is int)) throw new ArgumentException(); 153 | if (!client.HasConfig || client?.Config == null) return null; 154 | var episodeinfo = client.GetTvEpisode((int) show, season, episode, withImages ? TvEpisodeMethods.Images : TvEpisodeMethods.Undefined); 155 | if (episodeinfo?.Name == null) return null; 156 | 157 | return new EpisodeInformation 158 | { 159 | AirDate = episodeinfo.AirDate, 160 | Image = String.IsNullOrWhiteSpace(episodeinfo.StillPath)?null : client.GetImageUrl("w600", episodeinfo.StillPath).AbsoluteUri, 161 | Images = episodeinfo.Images?.Stills, 162 | ProviderHomepage = "https://www.themoviedb.org/tv/" + ((int)show) + "/season/" + season + "/episode/"+episode, 163 | PublisherHomepage = null, 164 | Title = episodeinfo.Name, 165 | Overview = episodeinfo.Overview 166 | 167 | }; 168 | } 169 | 170 | public String GetFirstImage(object images) 171 | { 172 | if (images == null) return null; 173 | if (!(images is List)) throw new ArgumentException(); 174 | if (!client.HasConfig || client?.Config == null) return null; 175 | var i = (List) images; 176 | if (i.Count == 0) return null; 177 | return client.GetImageUrl("original", i.First().FilePath).AbsoluteUri; 178 | } 179 | 180 | public String GetImage(int? maxwidth = null, int? maxheight = null) 181 | { 182 | throw new NotImplementedException(); 183 | } 184 | } 185 | } -------------------------------------------------------------------------------- /SjUpdater/ViewModel/ShowTileViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.Eventing.Reader; 4 | using System.Linq; 5 | using System.Windows; 6 | using System.Windows.Threading; 7 | using SjUpdater.Annotations; 8 | using SjUpdater.Model; 9 | using SjUpdater.Utils; 10 | 11 | namespace SjUpdater.ViewModel 12 | { 13 | public class ShowTileViewModel : PropertyChangedImpl 14 | { 15 | private readonly FavShowData _show; 16 | private CachedImage _bitmap; 17 | private String _title; 18 | private String _numberText; 19 | private String _nextText; 20 | private String _prevText; 21 | private String _bottomText; 22 | private String _newsText; 23 | private Visibility _bottomVisible = Visibility.Collapsed; 24 | private Visibility _isLoadingVisible = Visibility.Collapsed; 25 | private ShowViewModel _showViewModel; 26 | private readonly Dispatcher _dispatcher; 27 | 28 | public ShowTileViewModel(FavShowData show) 29 | { 30 | _dispatcher = Dispatcher.CurrentDispatcher; 31 | _show = show; 32 | // _showViewModel = new ShowViewModel(_show); 33 | 34 | Title= _show.Name; 35 | IsLoadingVisible = (_show.IsLoading) ? Visibility.Visible : Visibility.Collapsed; 36 | RecalcText(); 37 | RecalcNextPrevEpText(); 38 | Background = !string.IsNullOrWhiteSpace(_show.Cover) ? new CachedImage(_show.Cover) : null; 39 | _show.PropertyChanged += _show_PropertyChanged; 40 | 41 | } 42 | 43 | void _show_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) 44 | { 45 | switch (e.PropertyName) 46 | { 47 | case nameof(FavShowData.Name): 48 | Title = _show.Name; 49 | break; 50 | case nameof(FavShowData.Cover): 51 | _dispatcher.Invoke(delegate 52 | { 53 | Background = new CachedImage(_show.Cover); 54 | }); 55 | break; 56 | case nameof(FavShowData.NumberOfEpisodes): 57 | case nameof(FavShowData.NumberOfSeasons): 58 | RecalcText(); 59 | break; 60 | case nameof(FavShowData.IsLoading): 61 | IsLoadingVisible = (_show.IsLoading) ? Visibility.Visible : Visibility.Collapsed; 62 | break; 63 | default: 64 | if (String.IsNullOrEmpty(e.PropertyName) || e.PropertyName == nameof(FavShowData.Status) || e.PropertyName.StartsWith("NextEpisode") || 65 | e.PropertyName.StartsWith("PreviousEpisode") || e.PropertyName == nameof(FavShowData.NewEpisodes) || e.PropertyName == nameof(FavShowData.NewUpdates)) 66 | { 67 | RecalcNextPrevEpText(); 68 | } 69 | break; 70 | } 71 | } 72 | 73 | public void RecalcNextPrevEpText() 74 | { 75 | String next=""; 76 | String prev = ""; 77 | Visibility bottomVis = Visibility.Collapsed; 78 | String bottomText =""; 79 | String newsText = ""; 80 | if (_show.Status == null) return; 81 | if (_show.Status == "Ended" || _show.Status == "Canceled") 82 | { 83 | next = "Show Ended"; 84 | prev = "Final Episode aired "; 85 | } 86 | if (_show.NextEpisodeDate.HasValue) 87 | { 88 | if (_show.NextEpisodeSeasonNr == 1 && _show.NextEpisodeEpisodeNr == 1) 89 | { 90 | next = "Pilot airs "; 91 | } 92 | else if (_show.NextEpisodeEpisodeNr == 1) 93 | { 94 | next = "S" + _show.NextEpisodeSeasonNr + " airs "; 95 | } 96 | else 97 | { 98 | next = "S" + _show.NextEpisodeSeasonNr + "E" + _show.NextEpisodeEpisodeNr + " airs "; 99 | } 100 | next += FormatDate(_show.NextEpisodeDate.Value); 101 | 102 | bottomText = next; 103 | bottomVis = Visibility.Visible; 104 | } 105 | else if(_show.Status == "Returning Series") 106 | { 107 | next = "Next air date unknown"; 108 | } 109 | 110 | if (_show.PreviousEpisodeDate.HasValue) 111 | { 112 | if (_show.Status == "Returning Series") 113 | { 114 | prev = "S" + _show.PreviousEpisodeSeasonNr + "E" + _show.PreviousEpisodeEpisodeNr + " aired "; 115 | } 116 | prev += FormatDate(_show.PreviousEpisodeDate.Value); 117 | } 118 | 119 | if (_show.NewEpisodes) 120 | { 121 | var eps = _show.Seasons.SelectMany(s => s.Episodes.Where(e => e.NewEpisode)).ToList(); 122 | if (eps.Any()) 123 | { 124 | bottomText = "New:" + FormatEpisodes(eps, Show.NewUpdates?4:10); 125 | bottomVis = Visibility.Visible; 126 | newsText = "New:" + FormatEpisodes(eps, 10); 127 | if (Show.NewUpdates) 128 | { 129 | bottomText += " +Updates"; 130 | newsText += "\r\nUpdated:"; 131 | newsText += FormatEpisodes(_show.Seasons.SelectMany(s => s.Episodes.Where(e => e.NewUpdate)).ToList(), 10); 132 | } 133 | } 134 | } else if (_show.NewUpdates) 135 | { 136 | var eps = _show.Seasons.SelectMany(s => s.Episodes.Where(e => e.NewUpdate)).ToList(); 137 | if (eps.Any()) 138 | { 139 | bottomText = "Updated:" + FormatEpisodes(eps, 10); 140 | newsText += "Updated:" + FormatEpisodes(eps, 10); 141 | bottomVis = Visibility.Visible; 142 | } 143 | } 144 | 145 | NextText = next; 146 | PrevText = prev; 147 | NewsText = newsText; 148 | BottomText = bottomText; 149 | BottomVisible = bottomVis; 150 | } 151 | 152 | /// 153 | /// Formats a date relativ to today 154 | /// Example output: today, tomorrow, yesterday, 5 days ago, in 3 days, in 3 months, 1 year ago, in 10 years 155 | /// 156 | /// 157 | /// 158 | private string FormatDate(DateTime t) 159 | { 160 | string text = ""; 161 | int days = (DateTime.Today - t.Date).Days; 162 | bool future = t.Date > DateTime.Today; 163 | if (days==0) return "today"; 164 | if (future) 165 | { 166 | text = "in "; 167 | days = (t.Date - DateTime.Today).Days; 168 | } 169 | if (days == 1) return future ? "tomorrow" : "yesterday"; 170 | if (days < 30) 171 | { 172 | text += days + " days"; 173 | } else if (days < 360) 174 | { 175 | int months = days/30; 176 | text += months + " month"; 177 | if (months > 1) text += "s"; 178 | } 179 | else 180 | { 181 | int years = days/360; 182 | text += years + " year"; 183 | if (years > 1) text += "s"; 184 | } 185 | 186 | if (!future) text += " ago"; 187 | return text; 188 | } 189 | 190 | /// 191 | /// Formats the shortnames of a bunch of episodes 192 | /// Example output: "S1E2 S3E4 ..." 193 | /// 194 | /// 195 | /// 196 | /// 197 | private string FormatEpisodes(List eps, int max) 198 | { 199 | string text = ""; 200 | for (int i = 0; i < Math.Min(max, eps.Count); i++) 201 | { 202 | text += " S" + eps[i].Season.Number + "E" + eps[i].Number; 203 | } 204 | if (eps.Count > max) text += "..."; 205 | return text; 206 | } 207 | 208 | private void RecalcText() 209 | { 210 | String n = _show.NumberOfEpisodes + " "; 211 | n += _show.NumberOfEpisodes > 1 ? "Episodes" : "Episode"; 212 | n += " in " + _show.NumberOfSeasons + " "; 213 | n += _show.NumberOfSeasons > 1 ? "Seasons" : "Season"; 214 | NumberText = n; 215 | } 216 | 217 | 218 | public FavShowData Show => _show; 219 | 220 | public ShowViewModel ShowViewModel => _showViewModel ?? (_showViewModel = new ShowViewModel(_show)); 221 | 222 | public string NumberText 223 | { 224 | get { return _numberText; } 225 | 226 | private set 227 | { 228 | _numberText = value; 229 | OnPropertyChanged(); 230 | } 231 | } 232 | 233 | public String NextText 234 | { 235 | get { return _nextText;} 236 | private set 237 | { 238 | _nextText = value; 239 | OnPropertyChanged(); 240 | } 241 | } 242 | 243 | public String PrevText 244 | { 245 | get { return _prevText; } 246 | private set 247 | { 248 | _prevText = value; 249 | OnPropertyChanged(); 250 | } 251 | } 252 | 253 | public String NewsText 254 | { 255 | get { return _newsText;} 256 | private set 257 | { 258 | _newsText = value; 259 | OnPropertyChanged(); 260 | } 261 | } 262 | 263 | public String BottomText 264 | { 265 | get { return _bottomText;} 266 | set 267 | { 268 | _bottomText = value; 269 | OnPropertyChanged(); 270 | } 271 | } 272 | 273 | public Visibility BottomVisible 274 | { 275 | get { return _bottomVisible; } 276 | set 277 | { 278 | if (Settings.Instance.EnableImages) 279 | { 280 | _bottomVisible = value; 281 | OnPropertyChanged(); 282 | } 283 | } 284 | } 285 | 286 | public Visibility IsLoadingVisible 287 | { 288 | get { return _isLoadingVisible; } 289 | 290 | private set 291 | { 292 | _isLoadingVisible = value; 293 | OnPropertyChanged(); 294 | } 295 | } 296 | 297 | public string Title 298 | { 299 | get { return _title; } 300 | 301 | private set 302 | { 303 | _title = value; 304 | OnPropertyChanged(); 305 | } 306 | } 307 | 308 | 309 | public CachedImage Background 310 | { 311 | get { return _bitmap; } 312 | 313 | private set 314 | { 315 | _bitmap = value; 316 | OnPropertyChanged(); 317 | } 318 | } 319 | 320 | public Visibility BackgroundImageVisibility => Settings.Instance.EnableImages? Visibility.Visible : Visibility.Collapsed; 321 | public Visibility OverlayDefaultVisibility => Settings.Instance.EnableImages? Visibility.Collapsed : Visibility.Visible; 322 | 323 | } 324 | } 325 | --------------------------------------------------------------------------------