├── .gitattributes ├── .gitignore ├── Nandaka.Common ├── ExtendedWebClient.cs ├── GenericEqualityComparer.cs ├── LimitedConcurrencyLevelTaskScheduler .cs ├── ListStringConverter.cs ├── Nandaka.Common.csproj ├── Properties │ ├── AssemblyInfo.cs │ ├── Settings.Designer.cs │ └── Settings.settings ├── SecureIt.cs ├── Util.cs ├── WPF │ └── ZoomBorder.cs ├── WebImgConverter.cs └── app.config ├── Nijie.Test ├── App.config ├── Nijie.Test.csproj ├── Properties │ ├── AssemblyInfo.cs │ ├── Settings.Designer.cs │ └── Settings.settings ├── UnitTest1.cs ├── UpdateHtmlForm.Designer.cs ├── UpdateHtmlForm.cs ├── UpdateHtmlForm.resx ├── packages.config └── testpage │ ├── image-doujin-popup.html │ ├── image-doujin.html │ ├── image-manga-filter.html │ ├── image-manga-popup-filter.html │ ├── image-manga-popup.html │ ├── image-manga.html │ ├── image-normal.html │ ├── image-video-popup.html │ ├── image-video.html │ ├── image.html │ ├── index.html │ ├── member-bookmarked-images-lastpage.html │ ├── member-bookmarked-images.html │ ├── member-doujins.html │ ├── member-images.html │ ├── search-tag-exact-latest-lastpage.html │ ├── search-tag-exact-latest.html │ ├── search-tag-partial-latest-lastpage.html │ └── search-tag-partial-latest.html ├── NijieDownloader.Library ├── App.config ├── DAL │ └── NijieContext.cs ├── Migrations │ ├── 201402201406506_InitialCreate.Designer.cs │ ├── 201402201406506_InitialCreate.cs │ ├── 201402201406506_InitialCreate.resx │ ├── 201403061411087_AddNijieTags.Designer.cs │ ├── 201403061411087_AddNijieTags.cs │ ├── 201403061411087_AddNijieTags.resx │ ├── 201404021353243_1.0.3.1.Designer.cs │ ├── 201404021353243_1.0.3.1.cs │ ├── 201404021353243_1.0.3.1.resx │ └── Configuration.cs ├── Model │ ├── NijieImage.cs │ ├── NijieLoginInfo.cs │ ├── NijieMangaInfo.cs │ ├── NijieMember.cs │ ├── NijieSearch.cs │ ├── NijieSearchOption.cs │ └── NijieTag.cs ├── Nijie.Bookmark.cs ├── Nijie.Image.cs ├── Nijie.Login.cs ├── Nijie.Member.cs ├── Nijie.Search.cs ├── Nijie.cs ├── NijieConstants.cs ├── NijieDownloader.Library.csproj ├── NijieException.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Settings.Designer.cs │ └── Settings.settings └── packages.config ├── NijieDownloader.UI ├── App.xaml ├── App.xaml.cs ├── ImageLoader.cs ├── JobRunner.cs ├── JobStatusToVisibilityConverter.cs ├── JobTypeToVisibilityConverter.cs ├── Main │ ├── BatchDownloadPage.xaml │ ├── BatchDownloadPage.xaml.cs │ ├── BookmarkPage.xaml │ ├── BookmarkPage.xaml.cs │ ├── ImagePage.xaml │ ├── ImagePage.xaml.cs │ ├── MemberPage.xaml │ ├── MemberPage.xaml.cs │ ├── Popup │ │ ├── AddJobPopup.xaml │ │ └── AddJobPopup.xaml.cs │ ├── SearchPage.xaml │ └── SearchPage.xaml.cs ├── MainWindow.UIHelper.cs ├── MainWindow.xaml ├── MainWindow.xaml.cs ├── NijieDownloader.UI.csproj ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── Resources │ ├── donate.gif │ ├── error_icon.png │ ├── friends.png │ ├── loading.png │ ├── no_avatar.jpg │ ├── queued.png │ ├── thumbnail_anime.png │ └── thumbnail_comic.png ├── Settings │ ├── About.xaml │ ├── About.xaml.cs │ ├── Download.xaml │ ├── Download.xaml.cs │ ├── LoginInfo.xaml │ ├── LoginInfo.xaml.cs │ ├── Network.xaml │ └── Network.xaml.cs ├── ViewModel │ ├── JobDownloadViewModel.cs │ ├── JobDownloadViewModelComparer.cs │ ├── NijieImageViewModel.cs │ ├── NijieMemberBookmarkViewModel.cs │ ├── NijieMemberViewModel.cs │ ├── NijieSearchViewModel.cs │ ├── ViewModelBase.cs │ └── ViewModelHelper.cs ├── app.config ├── nijie_icon.ico └── packages.config ├── NijieDownloader.sln ├── NijieDownloader.vsmdi ├── README.txt ├── TestImage.txt ├── changelog.txt └── packages └── repositories.config /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | 24 | Nijie.Test/testpage/*.htm linguist-vendored=False 25 | Nijie.Test/testpage/*.html linguist-vendored=False -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | packages/* 2 | 3 | ################# 4 | ## Eclipse 5 | ################# 6 | 7 | *.pydevproject 8 | .project 9 | .metadata 10 | bin/ 11 | tmp/ 12 | *.tmp 13 | *.bak 14 | *.swp 15 | *~.nib 16 | local.properties 17 | .classpath 18 | .settings/ 19 | .loadpath 20 | 21 | # External tool builders 22 | .externalToolBuilders/ 23 | 24 | # Locally stored "Eclipse launch configurations" 25 | *.launch 26 | 27 | # CDT-specific 28 | .cproject 29 | 30 | # PDT-specific 31 | .buildpath 32 | 33 | 34 | ################# 35 | ## Visual Studio 36 | ################# 37 | 38 | ## Ignore Visual Studio temporary files, build results, and 39 | ## files generated by popular Visual Studio add-ons. 40 | 41 | # User-specific files 42 | *.suo 43 | *.user 44 | *.sln.docstates 45 | 46 | # Build results 47 | [Dd]ebug/ 48 | [Rr]elease/ 49 | *_i.c 50 | *_p.c 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.vspscc 65 | .builds 66 | *.dotCover 67 | 68 | ## TODO: If you have NuGet Package Restore enabled, uncomment this 69 | #packages/ 70 | 71 | # Visual C++ cache files 72 | ipch/ 73 | *.aps 74 | *.ncb 75 | *.opensdf 76 | *.sdf 77 | 78 | # Visual Studio profiler 79 | *.psess 80 | *.vsp 81 | 82 | # ReSharper is a .NET coding add-in 83 | _ReSharper* 84 | 85 | # Installshield output folder 86 | [Ee]xpress 87 | 88 | # DocProject is a documentation generator add-in 89 | DocProject/buildhelp/ 90 | DocProject/Help/*.HxT 91 | DocProject/Help/*.HxC 92 | DocProject/Help/*.hhc 93 | DocProject/Help/*.hhk 94 | DocProject/Help/*.hhp 95 | DocProject/Help/Html2 96 | DocProject/Help/html 97 | 98 | # Click-Once directory 99 | publish 100 | 101 | # Others 102 | [Bb]in 103 | [Oo]bj 104 | sql 105 | TestResults 106 | *.Cache 107 | ClientBin 108 | stylecop.* 109 | ~$* 110 | *.dbmdl 111 | Generated_Code #added for RIA/Silverlight projects 112 | 113 | # Backup & report files from converting an old project file to a newer 114 | # Visual Studio version. Backup files are not needed, because we have git ;-) 115 | _UpgradeReport_Files/ 116 | Backup*/ 117 | UpgradeLog*.XML 118 | 119 | 120 | 121 | ############ 122 | ## Windows 123 | ############ 124 | 125 | # Windows image file caches 126 | Thumbs.db 127 | 128 | # Folder config file 129 | Desktop.ini 130 | 131 | 132 | ############# 133 | ## Python 134 | ############# 135 | 136 | *.py[co] 137 | 138 | # Packages 139 | *.egg 140 | *.egg-info 141 | dist 142 | build 143 | eggs 144 | parts 145 | bin 146 | var 147 | sdist 148 | develop-eggs 149 | .installed.cfg 150 | 151 | # Installer logs 152 | pip-log.txt 153 | 154 | # Unit test / coverage reports 155 | .coverage 156 | .tox 157 | 158 | #Translations 159 | *.mo 160 | 161 | #Mr Developer 162 | .mr.developer.cfg 163 | 164 | # Mac crap 165 | .DS_Store 166 | *.7z 167 | list.txt 168 | nijie_icon.ico 169 | -------------------------------------------------------------------------------- /Nandaka.Common/GenericEqualityComparer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Nandaka.Common 7 | { 8 | /// 9 | /// http://stackoverflow.com/a/10720211 10 | /// 11 | /// 12 | public class EqualityComparer : IEqualityComparer 13 | { 14 | public EqualityComparer(Func cmp) 15 | { 16 | this.cmp = cmp; 17 | } 18 | public bool Equals(T x, T y) 19 | { 20 | return cmp(x, y); 21 | } 22 | 23 | public int GetHashCode(T obj) 24 | { 25 | return obj.GetHashCode(); 26 | } 27 | 28 | private Func cmp { get; set; } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Nandaka.Common/LimitedConcurrencyLevelTaskScheduler .cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace Nandaka.Common 9 | { 10 | /// 11 | /// Provides a task scheduler that ensures a maximum concurrency level while 12 | /// running on top of the thread pool. 13 | /// http://msdn.microsoft.com/en-us/library/ee789351(v=vs.110).aspx 14 | /// 15 | public class LimitedConcurrencyLevelTaskScheduler : TaskScheduler 16 | { 17 | // Indicates whether the current thread is processing work items. 18 | [ThreadStatic] 19 | private static bool _currentThreadIsProcessingItems; 20 | 21 | // The list of tasks to be executed 22 | private readonly LinkedList _tasks = new LinkedList(); // protected by lock(_tasks) 23 | 24 | // The maximum concurrency level allowed by this scheduler. 25 | private readonly int _maxDegreeOfParallelism; 26 | 27 | // Indicates whether the scheduler is currently processing work items. 28 | private int _delegatesQueuedOrRunning = 0; 29 | 30 | // Creates a new instance with the specified degree of parallelism. 31 | public LimitedConcurrencyLevelTaskScheduler(int maxDegreeOfParallelism, int hardLimit) 32 | { 33 | if (maxDegreeOfParallelism < 1) throw new ArgumentOutOfRangeException("maxDegreeOfParallelism"); 34 | if (maxDegreeOfParallelism > 0 && maxDegreeOfParallelism <= hardLimit) 35 | _maxDegreeOfParallelism = maxDegreeOfParallelism; 36 | else 37 | _maxDegreeOfParallelism = 2; 38 | } 39 | 40 | // Queues a task to the scheduler. 41 | protected sealed override void QueueTask(Task task) 42 | { 43 | // Add the task to the list of tasks to be processed. If there aren't enough 44 | // delegates currently queued or running to process tasks, schedule another. 45 | lock (_tasks) 46 | { 47 | _tasks.AddLast(task); 48 | if (_delegatesQueuedOrRunning < _maxDegreeOfParallelism) 49 | { 50 | ++_delegatesQueuedOrRunning; 51 | NotifyThreadPoolOfPendingWork(); 52 | } 53 | } 54 | } 55 | 56 | // Inform the ThreadPool that there's work to be executed for this scheduler. 57 | private void NotifyThreadPoolOfPendingWork() 58 | { 59 | ThreadPool.UnsafeQueueUserWorkItem(_ => 60 | { 61 | // Note that the current thread is now processing work items. 62 | // This is necessary to enable inlining of tasks into this thread. 63 | _currentThreadIsProcessingItems = true; 64 | try 65 | { 66 | // Process all available items in the queue. 67 | while (true) 68 | { 69 | Task item; 70 | lock (_tasks) 71 | { 72 | // When there are no more items to be processed, 73 | // note that we're done processing, and get out. 74 | if (_tasks.Count == 0) 75 | { 76 | --_delegatesQueuedOrRunning; 77 | break; 78 | } 79 | 80 | // Get the next item from the queue 81 | item = _tasks.First.Value; 82 | _tasks.RemoveFirst(); 83 | } 84 | 85 | // Execute the task we pulled out of the queue 86 | base.TryExecuteTask(item); 87 | } 88 | } 89 | // We're done processing items on the current thread 90 | finally { _currentThreadIsProcessingItems = false; } 91 | }, null); 92 | } 93 | 94 | // Attempts to execute the specified task on the current thread. 95 | protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) 96 | { 97 | // If this thread isn't already processing a task, we don't support inlining 98 | if (!_currentThreadIsProcessingItems) return false; 99 | 100 | // If the task was previously queued, remove it from the queue 101 | if (taskWasPreviouslyQueued) 102 | // Try to run the task. 103 | if (TryDequeue(task)) 104 | return base.TryExecuteTask(task); 105 | else 106 | return false; 107 | else 108 | return base.TryExecuteTask(task); 109 | } 110 | 111 | // Attempt to remove a previously scheduled task from the scheduler. 112 | protected sealed override bool TryDequeue(Task task) 113 | { 114 | lock (_tasks) return _tasks.Remove(task); 115 | } 116 | 117 | // Gets the maximum concurrency level supported by this scheduler. 118 | public sealed override int MaximumConcurrencyLevel { get { return _maxDegreeOfParallelism; } } 119 | 120 | // Gets an enumerable of the tasks currently scheduled on this scheduler. 121 | protected sealed override IEnumerable GetScheduledTasks() 122 | { 123 | bool lockTaken = false; 124 | try 125 | { 126 | Monitor.TryEnter(_tasks, ref lockTaken); 127 | if (lockTaken) return _tasks; 128 | else throw new NotSupportedException(); 129 | } 130 | finally 131 | { 132 | if (lockTaken) Monitor.Exit(_tasks); 133 | } 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /Nandaka.Common/ListStringConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Windows.Data; 6 | 7 | namespace Nandaka.Common 8 | { 9 | [ValueConversion(typeof(List), typeof(string))] 10 | public class ListToStringConverter : IValueConverter 11 | { 12 | 13 | public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 14 | { 15 | if (targetType != typeof(string)) 16 | throw new InvalidOperationException("The target must be a String"); 17 | 18 | var temp = value as List; 19 | if (temp == null || temp.Count == 0) return ""; 20 | 21 | var delimiter = ", "; 22 | if (parameter != null && parameter.GetType() == typeof(string)) 23 | { 24 | if (!String.IsNullOrWhiteSpace(parameter as string)) 25 | delimiter = parameter as string; 26 | } 27 | return String.Join(delimiter, ((List)value).ToArray()); 28 | } 29 | 30 | public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 31 | { 32 | if (targetType != typeof(List)) 33 | throw new InvalidOperationException("The target must be a List"); 34 | 35 | string delimiter = ", "; 36 | if (parameter != null && parameter.GetType() == typeof(string)) 37 | { 38 | if (!String.IsNullOrWhiteSpace(parameter as string)) 39 | delimiter = parameter as string; 40 | } 41 | 42 | var temp = value as string; 43 | var result = new List(); 44 | if (!string.IsNullOrWhiteSpace(temp)) 45 | { 46 | result = temp.Split(new string[] { delimiter }, StringSplitOptions.None).ToList(); 47 | } 48 | return result; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Nandaka.Common/Nandaka.Common.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {679DC8EF-673E-40CC-8478-08B3CD4BA836} 9 | Library 10 | Properties 11 | Nandaka.Common 12 | Nandaka.Common 13 | v4.5 14 | 512 15 | 16 | 17 | 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | false 26 | 27 | 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | false 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | Component 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | True 65 | True 66 | Settings.settings 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | PublicSettingsSingleFileGenerator 76 | Settings.Designer.cs 77 | 78 | 79 | 80 | 87 | -------------------------------------------------------------------------------- /Nandaka.Common/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Nandaka.Common")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Nandaka.Common")] 13 | [assembly: AssemblyCopyright("Copyright © Nandaka 2014-2021")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("a08cd296-1cf9-4770-ba74-01e98d84d508")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.5")] 36 | [assembly: AssemblyFileVersion("1.0.0.5")] 37 | -------------------------------------------------------------------------------- /Nandaka.Common/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Nandaka.Common.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.9.0.0")] 16 | public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | 26 | [global::System.Configuration.UserScopedSettingAttribute()] 27 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 28 | [global::System.Configuration.DefaultSettingValueAttribute("60000")] 29 | public int Timeout { 30 | get { 31 | return ((int)(this["Timeout"])); 32 | } 33 | set { 34 | this["Timeout"] = value; 35 | } 36 | } 37 | 38 | [global::System.Configuration.UserScopedSettingAttribute()] 39 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 40 | [global::System.Configuration.DefaultSettingValueAttribute("False")] 41 | public bool UseProxy { 42 | get { 43 | return ((bool)(this["UseProxy"])); 44 | } 45 | set { 46 | this["UseProxy"] = value; 47 | } 48 | } 49 | 50 | [global::System.Configuration.UserScopedSettingAttribute()] 51 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 52 | [global::System.Configuration.DefaultSettingValueAttribute("")] 53 | public string ProxyAddress { 54 | get { 55 | return ((string)(this["ProxyAddress"])); 56 | } 57 | set { 58 | this["ProxyAddress"] = value; 59 | } 60 | } 61 | 62 | [global::System.Configuration.UserScopedSettingAttribute()] 63 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 64 | [global::System.Configuration.DefaultSettingValueAttribute("0")] 65 | public int ProxyPort { 66 | get { 67 | return ((int)(this["ProxyPort"])); 68 | } 69 | set { 70 | this["ProxyPort"] = value; 71 | } 72 | } 73 | 74 | [global::System.Configuration.UserScopedSettingAttribute()] 75 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 76 | [global::System.Configuration.DefaultSettingValueAttribute("False")] 77 | public bool UseProxyLogin { 78 | get { 79 | return ((bool)(this["UseProxyLogin"])); 80 | } 81 | set { 82 | this["UseProxyLogin"] = value; 83 | } 84 | } 85 | 86 | [global::System.Configuration.UserScopedSettingAttribute()] 87 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 88 | [global::System.Configuration.DefaultSettingValueAttribute("")] 89 | public string ProxyUsername { 90 | get { 91 | return ((string)(this["ProxyUsername"])); 92 | } 93 | set { 94 | this["ProxyUsername"] = value; 95 | } 96 | } 97 | 98 | [global::System.Configuration.UserScopedSettingAttribute()] 99 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 100 | [global::System.Configuration.DefaultSettingValueAttribute("")] 101 | public string ProxyPassword { 102 | get { 103 | return ((string)(this["ProxyPassword"])); 104 | } 105 | set { 106 | this["ProxyPassword"] = value; 107 | } 108 | } 109 | 110 | [global::System.Configuration.UserScopedSettingAttribute()] 111 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 112 | [global::System.Configuration.DefaultSettingValueAttribute("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome" + 113 | "/31.0.1650.63 Safari/537.36")] 114 | public string UserAgent { 115 | get { 116 | return ((string)(this["UserAgent"])); 117 | } 118 | set { 119 | this["UserAgent"] = value; 120 | } 121 | } 122 | 123 | [global::System.Configuration.UserScopedSettingAttribute()] 124 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 125 | [global::System.Configuration.DefaultSettingValueAttribute("False")] 126 | public bool PadUserAgent { 127 | get { 128 | return ((bool)(this["PadUserAgent"])); 129 | } 130 | set { 131 | this["PadUserAgent"] = value; 132 | } 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /Nandaka.Common/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 60000 7 | 8 | 9 | False 10 | 11 | 12 | 13 | 14 | 15 | 0 16 | 17 | 18 | False 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36 28 | 29 | 30 | False 31 | 32 | 33 | -------------------------------------------------------------------------------- /Nandaka.Common/SecureIt.cs: -------------------------------------------------------------------------------- 1 | /*** 2 | * Taken from http://stackoverflow.com/a/8875545 3 | */ 4 | 5 | using System.Collections.Generic; 6 | using System.Runtime.InteropServices; 7 | using System.Security; 8 | using System.Security.Cryptography; 9 | using System.Text; 10 | using System; 11 | 12 | namespace Nandaka.Common 13 | { 14 | public static class SecureIt 15 | { 16 | private static readonly byte[] entropy = Encoding.Unicode.GetBytes("Salt Is Not A Password"); 17 | 18 | public static string EncryptString(this SecureString input) 19 | { 20 | if (input == null) 21 | { 22 | return null; 23 | } 24 | 25 | var encryptedData = ProtectedData.Protect( 26 | Encoding.Unicode.GetBytes(input.ToInsecureString()), 27 | entropy, 28 | DataProtectionScope.CurrentUser); 29 | 30 | return Convert.ToBase64String(encryptedData); 31 | } 32 | 33 | public static SecureString DecryptString(this string encryptedData) 34 | { 35 | if (encryptedData == null) 36 | { 37 | return null; 38 | } 39 | 40 | try 41 | { 42 | var decryptedData = ProtectedData.Unprotect( 43 | Convert.FromBase64String(encryptedData), 44 | entropy, 45 | DataProtectionScope.CurrentUser); 46 | 47 | return Encoding.Unicode.GetString(decryptedData).ToSecureString(); 48 | } 49 | catch 50 | { 51 | return new SecureString(); 52 | } 53 | } 54 | 55 | public static SecureString ToSecureString(this IEnumerable input) 56 | { 57 | if (input == null) 58 | { 59 | return null; 60 | } 61 | 62 | var secure = new SecureString(); 63 | 64 | foreach (var c in input) 65 | { 66 | secure.AppendChar(c); 67 | } 68 | 69 | secure.MakeReadOnly(); 70 | return secure; 71 | } 72 | 73 | public static string ToInsecureString(this SecureString input) 74 | { 75 | if (input == null) 76 | { 77 | return null; 78 | } 79 | 80 | var ptr = Marshal.SecureStringToBSTR(input); 81 | 82 | try 83 | { 84 | return Marshal.PtrToStringBSTR(ptr); 85 | } 86 | finally 87 | { 88 | Marshal.ZeroFreeBSTR(ptr); 89 | } 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Nandaka.Common/WPF/ZoomBorder.cs: -------------------------------------------------------------------------------- 1 | /*** 2 | * From https://github.com/wieslawsoltes/PanAndZoom/blob/master/PanAndZoom/ZoomBorder.cs 3 | ***/ 4 | 5 | using System.Linq; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Input; 9 | using System.Windows.Media; 10 | 11 | namespace Nandaka.Common.WPF 12 | { 13 | public class ZoomBorder : Border 14 | { 15 | private UIElement child = null; 16 | private Point origin; 17 | private Point start; 18 | 19 | private TranslateTransform GetTranslateTransform(UIElement element) 20 | { 21 | return (TranslateTransform)((TransformGroup)element.RenderTransform) 22 | .Children.First(tr => tr is TranslateTransform); 23 | } 24 | 25 | private ScaleTransform GetScaleTransform(UIElement element) 26 | { 27 | return (ScaleTransform)((TransformGroup)element.RenderTransform) 28 | .Children.First(tr => tr is ScaleTransform); 29 | } 30 | 31 | public override UIElement Child 32 | { 33 | get { return base.Child; } 34 | set 35 | { 36 | if (value != null && value != this.Child) 37 | this.Initialize(value); 38 | base.Child = value; 39 | } 40 | } 41 | 42 | public void Initialize(UIElement element) 43 | { 44 | this.child = element; 45 | 46 | if (child != null) 47 | { 48 | TransformGroup group = new TransformGroup(); 49 | ScaleTransform st = new ScaleTransform(); 50 | group.Children.Add(st); 51 | TranslateTransform tt = new TranslateTransform(); 52 | group.Children.Add(tt); 53 | child.RenderTransform = group; 54 | child.RenderTransformOrigin = new Point(0.0, 0.0); 55 | this.MouseWheel += child_MouseWheel; 56 | this.MouseLeftButtonDown += child_MouseLeftButtonDown; 57 | this.MouseLeftButtonUp += child_MouseLeftButtonUp; 58 | this.MouseMove += child_MouseMove; 59 | this.PreviewMouseRightButtonDown += new MouseButtonEventHandler( 60 | child_PreviewMouseRightButtonDown); 61 | this.MouseDown += new MouseButtonEventHandler(ZoomBorder_MouseDown); 62 | } 63 | } 64 | 65 | /// 66 | /// Reset zoom on double click 67 | /// 68 | /// 69 | /// 70 | private void ZoomBorder_MouseDown(object sender, MouseButtonEventArgs e) 71 | { 72 | if (e.ClickCount >= 2) 73 | { 74 | // reset zoom 75 | Reset(); 76 | } 77 | } 78 | 79 | public void Reset() 80 | { 81 | if (child != null) 82 | { 83 | // reset zoom 84 | var st = GetScaleTransform(child); 85 | st.ScaleX = 1.0; 86 | st.ScaleY = 1.0; 87 | 88 | // reset pan 89 | var tt = GetTranslateTransform(child); 90 | tt.X = 0.0; 91 | tt.Y = 0.0; 92 | } 93 | } 94 | 95 | #region Child Events 96 | 97 | private void child_MouseWheel(object sender, MouseWheelEventArgs e) 98 | { 99 | if (child != null) 100 | { 101 | var st = GetScaleTransform(child); 102 | var tt = GetTranslateTransform(child); 103 | 104 | double zoom = e.Delta > 0 ? .2 : -.2; 105 | if (!(e.Delta > 0) && (st.ScaleX < .4 || st.ScaleY < .4)) 106 | return; 107 | 108 | Point relative = e.GetPosition(child); 109 | double abosuluteX; 110 | double abosuluteY; 111 | 112 | abosuluteX = relative.X * st.ScaleX + tt.X; 113 | abosuluteY = relative.Y * st.ScaleY + tt.Y; 114 | 115 | st.ScaleX += zoom; 116 | st.ScaleY += zoom; 117 | 118 | tt.X = abosuluteX - relative.X * st.ScaleX; 119 | tt.Y = abosuluteY - relative.Y * st.ScaleY; 120 | } 121 | } 122 | 123 | private void child_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) 124 | { 125 | if (child != null) 126 | { 127 | var tt = GetTranslateTransform(child); 128 | start = e.GetPosition(this); 129 | origin = new Point(tt.X, tt.Y); 130 | this.Cursor = Cursors.Hand; 131 | child.CaptureMouse(); 132 | } 133 | } 134 | 135 | private void child_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) 136 | { 137 | if (child != null) 138 | { 139 | child.ReleaseMouseCapture(); 140 | this.Cursor = Cursors.Arrow; 141 | } 142 | } 143 | 144 | private void child_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e) 145 | { 146 | this.Reset(); 147 | } 148 | 149 | private void child_MouseMove(object sender, MouseEventArgs e) 150 | { 151 | if (child != null) 152 | { 153 | if (child.IsMouseCaptured) 154 | { 155 | var tt = GetTranslateTransform(child); 156 | Vector v = start - e.GetPosition(this); 157 | tt.X = origin.X - v.X; 158 | tt.Y = origin.Y - v.Y; 159 | } 160 | } 161 | } 162 | 163 | #endregion Child Events 164 | } 165 | } -------------------------------------------------------------------------------- /Nandaka.Common/WebImgConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | using System.Windows.Data; 7 | using System.Windows.Media.Imaging; 8 | using System.IO; 9 | 10 | 11 | namespace Nandaka.Common 12 | { 13 | public class WebImgConverter : IValueConverter 14 | { 15 | public object Convert(object value) 16 | { 17 | if (value != null) 18 | { 19 | var url = value.ToString(); 20 | if (url.StartsWith("//")) 21 | { 22 | url = "http:" + url; 23 | } 24 | 25 | //ExtendedWebClient client = new ExtendedWebClient(); 26 | //var result = client.DownloadData(url); 27 | //using (var ms = new MemoryStream(result)) 28 | //{ 29 | // var image = new BitmapImage(); 30 | // image.BeginInit(); 31 | // image.CacheOption = BitmapCacheOption.OnLoad; 32 | // image.StreamSource = ms; 33 | // image.EndInit(); 34 | // return image; 35 | //} 36 | BitmapImage bi = new BitmapImage(new Uri(url)); 37 | return bi; 38 | 39 | } 40 | else 41 | { 42 | return null; 43 | } 44 | 45 | } 46 | 47 | public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 48 | { 49 | return Convert(value); 50 | } 51 | 52 | public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 53 | { 54 | return Convert(value); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Nandaka.Common/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 60000 12 | 13 | 14 | False 15 | 16 | 17 | 18 | 19 | 20 | 0 21 | 22 | 23 | False 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36 33 | 34 | 35 | False 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Nijie.Test/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 |
8 |
11 |
14 | 15 |
16 | 17 |
20 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 60000 52 | 53 | 54 | False 55 | 56 | 57 | localhost> 58 | 59 | 60 | 8118 61 | 62 | 63 | False 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36 73 | 74 | 75 | False 76 | 77 | 78 | 79 | 80 | False 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /Nijie.Test/Nijie.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | 8 | 9 | 2.0 10 | {C64EC614-A577-42AB-A67A-21C85A698797} 11 | Library 12 | Properties 13 | NijieDownloader.Test 14 | Nijie.Test 15 | v4.5 16 | 512 17 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 18 | 19 | 20 | 21 | 22 | 23 | true 24 | full 25 | false 26 | bin\Debug\ 27 | DEBUG;TRACE 28 | prompt 29 | 4 30 | false 31 | 32 | 33 | pdbonly 34 | true 35 | bin\Release\ 36 | TRACE 37 | prompt 38 | 4 39 | false 40 | 41 | 42 | 43 | ..\packages\EntityFramework.6.4.4\lib\net45\EntityFramework.dll 44 | 45 | 46 | ..\packages\EntityFramework.6.4.4\lib\net45\EntityFramework.SqlServer.dll 47 | 48 | 49 | ..\packages\EntityFramework.SqlServerCompact.6.4.4\lib\net45\EntityFramework.SqlServerCompact.dll 50 | 51 | 52 | ..\packages\HtmlAgilityPack.1.11.31\lib\Net45\HtmlAgilityPack.dll 53 | 54 | 55 | ..\packages\log4net.2.0.12\lib\net45\log4net.dll 56 | 57 | 58 | 59 | 60 | 61 | 62 | 3.5 63 | 64 | 65 | 66 | 67 | ..\packages\Microsoft.SqlServer.Compact.4.0.8876.1\lib\net40\System.Data.SqlServerCe.dll 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | False 77 | 78 | 79 | 80 | 81 | 82 | True 83 | True 84 | Settings.settings 85 | 86 | 87 | 88 | Form 89 | 90 | 91 | UpdateHtmlForm.cs 92 | 93 | 94 | 95 | 96 | {679DC8EF-673E-40CC-8478-08B3CD4BA836} 97 | Nandaka.Common 98 | 99 | 100 | {6976B954-CD09-4FE6-9880-BF45150A6EF2} 101 | NijieDownloader.Library 102 | 103 | 104 | 105 | 106 | Designer 107 | 108 | 109 | 110 | SettingsSingleFileGenerator 111 | Settings.Designer.cs 112 | 113 | 114 | 115 | 116 | UpdateHtmlForm.cs 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | if not exist "$(TargetDir)x86" md "$(TargetDir)x86" 126 | xcopy /s /y "$(SolutionDir)packages\Microsoft.SqlServer.Compact.4.0.8876.1\NativeBinaries\x86\*.*" "$(TargetDir)x86" 127 | if not exist "$(TargetDir)amd64" md "$(TargetDir)amd64" 128 | xcopy /s /y "$(SolutionDir)packages\Microsoft.SqlServer.Compact.4.0.8876.1\NativeBinaries\amd64\*.*" "$(TargetDir)amd64" 129 | 130 | 131 | 132 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 133 | 134 | 135 | 136 | 137 | 138 | 145 | -------------------------------------------------------------------------------- /Nijie.Test/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("NijieDownloader.Test")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("NijieDownloader.Test")] 13 | [assembly: AssemblyCopyright("Copyright © Nandaka 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("d3cb862f-3d50-4c73-8e21-982e0a5e2d4b")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /Nijie.Test/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace NijieDownloader.Test.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.9.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | 26 | [global::System.Configuration.UserScopedSettingAttribute()] 27 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 28 | [global::System.Configuration.DefaultSettingValueAttribute("")] 29 | public string Username { 30 | get { 31 | return ((string)(this["Username"])); 32 | } 33 | set { 34 | this["Username"] = value; 35 | } 36 | } 37 | 38 | [global::System.Configuration.UserScopedSettingAttribute()] 39 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 40 | [global::System.Configuration.DefaultSettingValueAttribute("")] 41 | public string Password { 42 | get { 43 | return ((string)(this["Password"])); 44 | } 45 | set { 46 | this["Password"] = value; 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Nijie.Test/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Nijie.Test/UpdateHtmlForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace NijieDownloader.Test 2 | { 3 | partial class UpdateHtmlForm 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.btnUpdate = new System.Windows.Forms.Button(); 32 | this.txtLog = new System.Windows.Forms.TextBox(); 33 | this.txtUser = new System.Windows.Forms.TextBox(); 34 | this.txtPass = new System.Windows.Forms.TextBox(); 35 | this.label1 = new System.Windows.Forms.Label(); 36 | this.Pass = new System.Windows.Forms.Label(); 37 | this.btnLogin = new System.Windows.Forms.Button(); 38 | this.SuspendLayout(); 39 | // 40 | // btnUpdate 41 | // 42 | this.btnUpdate.Location = new System.Drawing.Point(465, 12); 43 | this.btnUpdate.Name = "btnUpdate"; 44 | this.btnUpdate.Size = new System.Drawing.Size(75, 23); 45 | this.btnUpdate.TabIndex = 0; 46 | this.btnUpdate.Text = "Update"; 47 | this.btnUpdate.UseVisualStyleBackColor = true; 48 | this.btnUpdate.Click += new System.EventHandler(this.btnUpdate_Click); 49 | // 50 | // txtLog 51 | // 52 | this.txtLog.Location = new System.Drawing.Point(12, 44); 53 | this.txtLog.Multiline = true; 54 | this.txtLog.Name = "txtLog"; 55 | this.txtLog.Size = new System.Drawing.Size(528, 206); 56 | this.txtLog.TabIndex = 1; 57 | // 58 | // txtUser 59 | // 60 | this.txtUser.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::NijieDownloader.Test.Properties.Settings.Default, "Username", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); 61 | this.txtUser.Location = new System.Drawing.Point(56, 12); 62 | this.txtUser.Name = "txtUser"; 63 | this.txtUser.Size = new System.Drawing.Size(136, 22); 64 | this.txtUser.TabIndex = 2; 65 | this.txtUser.Text = global::NijieDownloader.Test.Properties.Settings.Default.Username; 66 | // 67 | // txtPass 68 | // 69 | this.txtPass.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::NijieDownloader.Test.Properties.Settings.Default, "Password", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); 70 | this.txtPass.Location = new System.Drawing.Point(233, 12); 71 | this.txtPass.Name = "txtPass"; 72 | this.txtPass.Size = new System.Drawing.Size(163, 22); 73 | this.txtPass.TabIndex = 3; 74 | this.txtPass.Text = global::NijieDownloader.Test.Properties.Settings.Default.Password; 75 | // 76 | // label1 77 | // 78 | this.label1.AutoSize = true; 79 | this.label1.Location = new System.Drawing.Point(12, 15); 80 | this.label1.Name = "label1"; 81 | this.label1.Size = new System.Drawing.Size(30, 13); 82 | this.label1.TabIndex = 4; 83 | this.label1.Text = "User"; 84 | // 85 | // Pass 86 | // 87 | this.Pass.AutoSize = true; 88 | this.Pass.Location = new System.Drawing.Point(198, 17); 89 | this.Pass.Name = "Pass"; 90 | this.Pass.Size = new System.Drawing.Size(29, 13); 91 | this.Pass.TabIndex = 5; 92 | this.Pass.Text = "Pass"; 93 | // 94 | // btnLogin 95 | // 96 | this.btnLogin.Location = new System.Drawing.Point(402, 12); 97 | this.btnLogin.Name = "btnLogin"; 98 | this.btnLogin.Size = new System.Drawing.Size(57, 23); 99 | this.btnLogin.TabIndex = 6; 100 | this.btnLogin.Text = "Login"; 101 | this.btnLogin.UseVisualStyleBackColor = true; 102 | this.btnLogin.Click += new System.EventHandler(this.btnLogin_Click); 103 | // 104 | // UpdateHtmlForm 105 | // 106 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 107 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 108 | this.ClientSize = new System.Drawing.Size(552, 262); 109 | this.Controls.Add(this.btnLogin); 110 | this.Controls.Add(this.Pass); 111 | this.Controls.Add(this.label1); 112 | this.Controls.Add(this.txtPass); 113 | this.Controls.Add(this.txtUser); 114 | this.Controls.Add(this.txtLog); 115 | this.Controls.Add(this.btnUpdate); 116 | this.Name = "UpdateHtmlForm"; 117 | this.Text = "UpdateHtmlForm"; 118 | this.ResumeLayout(false); 119 | this.PerformLayout(); 120 | 121 | } 122 | 123 | #endregion 124 | 125 | private System.Windows.Forms.Button btnUpdate; 126 | private System.Windows.Forms.TextBox txtLog; 127 | private System.Windows.Forms.TextBox txtUser; 128 | private System.Windows.Forms.TextBox txtPass; 129 | private System.Windows.Forms.Label label1; 130 | private System.Windows.Forms.Label Pass; 131 | private System.Windows.Forms.Button btnLogin; 132 | } 133 | } -------------------------------------------------------------------------------- /Nijie.Test/UpdateHtmlForm.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /Nijie.Test/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /NijieDownloader.Library/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 |
8 | 9 | 10 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | True 35 | 36 | 37 | 3 38 | 39 | 40 | False 41 | 42 | 43 | False 44 | 45 | 46 | False 47 | 48 | 49 | 60 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /NijieDownloader.Library/DAL/NijieContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Data.Entity; 6 | using NijieDownloader.Library.Model; 7 | 8 | namespace NijieDownloader.Library.DAL 9 | { 10 | public class NijieContext : DbContext 11 | { 12 | public NijieContext() 13 | { 14 | Database.SetInitializer(new MigrateDatabaseToLatestVersion()); 15 | } 16 | 17 | public DbSet Images { get; set; } 18 | public DbSet Members { get; set; } 19 | public DbSet Tags { get; set; } 20 | public DbSet MangaInfo { get; set; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /NijieDownloader.Library/Migrations/201402201406506_InitialCreate.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | namespace NijieDownloader.Library.Migrations 3 | { 4 | using System.CodeDom.Compiler; 5 | using System.Data.Entity.Migrations; 6 | using System.Data.Entity.Migrations.Infrastructure; 7 | using System.Resources; 8 | 9 | [GeneratedCode("EntityFramework.Migrations", "6.0.2-21211")] 10 | public sealed partial class InitialCreate : IMigrationMetadata 11 | { 12 | private readonly ResourceManager Resources = new ResourceManager(typeof(InitialCreate)); 13 | 14 | string IMigrationMetadata.Id 15 | { 16 | get { return "201402201406506_InitialCreate"; } 17 | } 18 | 19 | string IMigrationMetadata.Source 20 | { 21 | get { return null; } 22 | } 23 | 24 | string IMigrationMetadata.Target 25 | { 26 | get { return Resources.GetString("Target"); } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /NijieDownloader.Library/Migrations/201402201406506_InitialCreate.cs: -------------------------------------------------------------------------------- 1 | namespace NijieDownloader.Library.Migrations 2 | { 3 | using System; 4 | using System.Data.Entity.Migrations; 5 | 6 | public partial class InitialCreate : DbMigration 7 | { 8 | public override void Up() 9 | { 10 | CreateTable( 11 | "dbo.NijieImages", 12 | c => new 13 | { 14 | ImageId = c.Int(nullable: false), 15 | BigImageUrl = c.String(maxLength: 4000), 16 | MediumImageUrl = c.String(maxLength: 4000), 17 | ThumbImageUrl = c.String(maxLength: 4000), 18 | Title = c.String(maxLength: 4000), 19 | Description = c.String(maxLength: 4000), 20 | WorkDate = c.DateTime(nullable: false), 21 | IsManga = c.Boolean(nullable: false), 22 | Referer = c.String(maxLength: 4000), 23 | IsFriendOnly = c.Boolean(nullable: false), 24 | IsGoldenMember = c.Boolean(nullable: false), 25 | NuitaCount = c.Int(nullable: false), 26 | GoodCount = c.Int(nullable: false), 27 | IsAnimated = c.Boolean(nullable: false), 28 | SavedFilename = c.String(maxLength: 4000), 29 | Member_MemberId = c.Int(), 30 | }) 31 | .PrimaryKey(t => t.ImageId) 32 | .ForeignKey("dbo.NijieMembers", t => t.Member_MemberId) 33 | .Index(t => t.Member_MemberId); 34 | 35 | CreateTable( 36 | "dbo.NijieMembers", 37 | c => new 38 | { 39 | MemberId = c.Int(nullable: false), 40 | UserName = c.String(maxLength: 4000), 41 | AvatarUrl = c.String(maxLength: 4000), 42 | }) 43 | .PrimaryKey(t => t.MemberId); 44 | 45 | } 46 | 47 | public override void Down() 48 | { 49 | DropForeignKey("dbo.NijieImages", "Member_MemberId", "dbo.NijieMembers"); 50 | DropIndex("dbo.NijieImages", new[] { "Member_MemberId" }); 51 | DropTable("dbo.NijieMembers"); 52 | DropTable("dbo.NijieImages"); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /NijieDownloader.Library/Migrations/201403061411087_AddNijieTags.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | namespace NijieDownloader.Library.Migrations 3 | { 4 | using System.CodeDom.Compiler; 5 | using System.Data.Entity.Migrations; 6 | using System.Data.Entity.Migrations.Infrastructure; 7 | using System.Resources; 8 | 9 | [GeneratedCode("EntityFramework.Migrations", "6.0.2-21211")] 10 | public sealed partial class AddNijieTags : IMigrationMetadata 11 | { 12 | private readonly ResourceManager Resources = new ResourceManager(typeof(AddNijieTags)); 13 | 14 | string IMigrationMetadata.Id 15 | { 16 | get { return "201403061411087_AddNijieTags"; } 17 | } 18 | 19 | string IMigrationMetadata.Source 20 | { 21 | get { return null; } 22 | } 23 | 24 | string IMigrationMetadata.Target 25 | { 26 | get { return Resources.GetString("Target"); } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /NijieDownloader.Library/Migrations/201403061411087_AddNijieTags.cs: -------------------------------------------------------------------------------- 1 | namespace NijieDownloader.Library.Migrations 2 | { 3 | using System; 4 | using System.Data.Entity.Migrations; 5 | 6 | public partial class AddNijieTags : DbMigration 7 | { 8 | public override void Up() 9 | { 10 | CreateTable( 11 | "dbo.NijieTags", 12 | c => new 13 | { 14 | Name = c.String(nullable: false, maxLength: 4000), 15 | }) 16 | .PrimaryKey(t => t.Name); 17 | 18 | CreateTable( 19 | "dbo.NijieTagNijieImages", 20 | c => new 21 | { 22 | NijieTag_Name = c.String(nullable: false, maxLength: 4000), 23 | NijieImage_ImageId = c.Int(nullable: false), 24 | }) 25 | .PrimaryKey(t => new { t.NijieTag_Name, t.NijieImage_ImageId }) 26 | .ForeignKey("dbo.NijieTags", t => t.NijieTag_Name, cascadeDelete: true) 27 | .ForeignKey("dbo.NijieImages", t => t.NijieImage_ImageId, cascadeDelete: true) 28 | .Index(t => t.NijieTag_Name) 29 | .Index(t => t.NijieImage_ImageId); 30 | 31 | } 32 | 33 | public override void Down() 34 | { 35 | DropForeignKey("dbo.NijieTagNijieImages", "NijieImage_ImageId", "dbo.NijieImages"); 36 | DropForeignKey("dbo.NijieTagNijieImages", "NijieTag_Name", "dbo.NijieTags"); 37 | DropIndex("dbo.NijieTagNijieImages", new[] { "NijieImage_ImageId" }); 38 | DropIndex("dbo.NijieTagNijieImages", new[] { "NijieTag_Name" }); 39 | DropTable("dbo.NijieTagNijieImages"); 40 | DropTable("dbo.NijieTags"); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /NijieDownloader.Library/Migrations/201404021353243_1.0.3.1.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | namespace NijieDownloader.Library.Migrations 3 | { 4 | using System.CodeDom.Compiler; 5 | using System.Data.Entity.Migrations; 6 | using System.Data.Entity.Migrations.Infrastructure; 7 | using System.Resources; 8 | 9 | [GeneratedCode("EntityFramework.Migrations", "6.1.0-30225")] 10 | public sealed partial class _1031 : IMigrationMetadata 11 | { 12 | private readonly ResourceManager Resources = new ResourceManager(typeof(_1031)); 13 | 14 | string IMigrationMetadata.Id 15 | { 16 | get { return "201404021353243_1.0.3.1"; } 17 | } 18 | 19 | string IMigrationMetadata.Source 20 | { 21 | get { return null; } 22 | } 23 | 24 | string IMigrationMetadata.Target 25 | { 26 | get { return Resources.GetString("Target"); } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /NijieDownloader.Library/Migrations/201404021353243_1.0.3.1.cs: -------------------------------------------------------------------------------- 1 | namespace NijieDownloader.Library.Migrations 2 | { 3 | using System; 4 | using System.Data.Entity.Migrations; 5 | 6 | public partial class _1031 : DbMigration 7 | { 8 | public override void Up() 9 | { 10 | //CreateIndex("dbo.NijieImages", "Member_MemberId"); 11 | //CreateIndex("dbo.NijieTagNijieImages", "NijieTag_Name"); 12 | //CreateIndex("dbo.NijieTagNijieImages", "NijieImage_ImageId"); 13 | } 14 | 15 | public override void Down() 16 | { 17 | //DropIndex("dbo.NijieTagNijieImages", new[] { "NijieImage_ImageId" }); 18 | //DropIndex("dbo.NijieTagNijieImages", new[] { "NijieTag_Name" }); 19 | //DropIndex("dbo.NijieImages", new[] { "Member_MemberId" }); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /NijieDownloader.Library/Migrations/Configuration.cs: -------------------------------------------------------------------------------- 1 | namespace NijieDownloader.Library.Migrations 2 | { 3 | using System; 4 | using System.Data.Entity; 5 | using System.Data.Entity.Migrations; 6 | using System.Linq; 7 | 8 | internal sealed class Configuration : DbMigrationsConfiguration 9 | { 10 | public Configuration() 11 | { 12 | AutomaticMigrationsEnabled = true; 13 | } 14 | 15 | protected override void Seed(NijieDownloader.Library.DAL.NijieContext context) 16 | { 17 | // This method will be called after migrating to the latest version. 18 | 19 | // You can use the DbSet.AddOrUpdate() helper extension method 20 | // to avoid creating duplicate seed data. E.g. 21 | // 22 | // context.People.AddOrUpdate( 23 | // p => p.FullName, 24 | // new Person { FullName = "Andrew Peters" }, 25 | // new Person { FullName = "Brice Lambson" }, 26 | // new Person { FullName = "Rowan Miller" } 27 | // ); 28 | // 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /NijieDownloader.Library/Model/NijieImage.cs: -------------------------------------------------------------------------------- 1 | using Nandaka.Common; 2 | using NijieDownloader.Library.DAL; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.ComponentModel.DataAnnotations; 6 | using System.ComponentModel.DataAnnotations.Schema; 7 | using System.Data.Entity.Migrations; 8 | using System.Diagnostics; 9 | using System.Linq; 10 | 11 | namespace NijieDownloader.Library.Model 12 | { 13 | public class NijieImage 14 | { 15 | #region ctor 16 | 17 | public NijieImage() 18 | { 19 | this.ImageId = -1; 20 | } 21 | 22 | public NijieImage(int imageId) 23 | { 24 | this.ImageId = imageId; 25 | } 26 | 27 | #endregion ctor 28 | 29 | #region db colums 30 | 31 | [Key] 32 | [DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None)] 33 | public int ImageId { get; set; } 34 | 35 | public string BigImageUrl { get; set; } 36 | 37 | public string MediumImageUrl { get; set; } 38 | 39 | public string ThumbImageUrl { get; set; } 40 | 41 | public string Title { get; set; } 42 | 43 | public string Description { get; set; } 44 | 45 | // to avoid overflow problem 46 | private DateTime _workDate; 47 | 48 | public DateTime WorkDate 49 | { 50 | get 51 | { 52 | if (_workDate == DateTime.MinValue) 53 | _workDate = System.Data.SqlTypes.SqlDateTime.MinValue.Value; 54 | return _workDate; 55 | } 56 | set 57 | { 58 | _workDate = value; 59 | } 60 | } 61 | 62 | public string Referer { get; set; } 63 | 64 | public bool IsFriendOnly { get; set; } 65 | 66 | public bool IsGoldenMember { get; set; } 67 | 68 | public int NuitaCount { get; set; } 69 | 70 | public int GoodCount { get; set; } 71 | 72 | public bool IsAnimated { get; set; } 73 | 74 | public string SavedFilename { get; set; } 75 | 76 | public string ServerFilename { get; set; } 77 | 78 | public long Filesize { get; set; } 79 | 80 | // manga related 81 | public bool IsManga { get; set; } 82 | 83 | public List ImageUrls { get; set; } 84 | 85 | public virtual ICollection MangaPages { get; set; } 86 | 87 | public virtual ICollection Tags { get; set; } 88 | 89 | public NijieMember Member { get; set; } 90 | 91 | #endregion db colums 92 | 93 | #region not mapped 94 | 95 | [NotMapped] 96 | public string ViewUrl 97 | { 98 | get 99 | { 100 | return Util.FixUrl("//nijie.info/view.php?id=" + ImageId, Nijie.ROOT_DOMAIN, Properties.Settings.Default.UseHttps); 101 | } 102 | private set { } 103 | } 104 | 105 | private bool _isDownloaded; 106 | 107 | [NotMapped] 108 | public bool IsDownloaded 109 | { 110 | get 111 | { 112 | if (_workDate == System.Data.SqlTypes.SqlDateTime.MinValue.Value || !_isDownloaded) 113 | return false; 114 | return _isDownloaded; 115 | } 116 | set 117 | { 118 | _isDownloaded = value; 119 | } 120 | } 121 | 122 | [NotMapped] 123 | public NijieMember BookmarkedBy { get; set; } 124 | 125 | #endregion not mapped 126 | 127 | public void SaveToDb(NijieContext dao, bool suppressSave = false) 128 | { 129 | var member = (from x in dao.Members 130 | where x.MemberId == this.Member.MemberId 131 | select x).FirstOrDefault(); 132 | if (member != null) 133 | { 134 | this.Member = member; 135 | } 136 | 137 | var temp = new List(); 138 | for (int i = 0; i < this.Tags.Count; ++i) 139 | { 140 | var t = this.Tags.ElementAt(i); 141 | var x = (from a in dao.Tags 142 | where a.Name == t.Name 143 | select a).FirstOrDefault(); 144 | if (x != null) 145 | { 146 | temp.Add(x); 147 | } 148 | else 149 | { 150 | temp.Add(t); 151 | } 152 | } 153 | this.Tags = temp; 154 | 155 | dao.Images.AddOrUpdate(this); 156 | 157 | if (!suppressSave) 158 | { 159 | dao.SaveChanges(); 160 | } 161 | 162 | Debug.Assert(this.WorkDate != DateTime.MinValue, "Works Date cannot be set to DateTime.MinValue"); 163 | } 164 | 165 | public static bool IsDownloadedInDB(int imageId) 166 | { 167 | using (var dao = new NijieContext()) 168 | { 169 | var image = (from i in dao.Images 170 | where i.ImageId == imageId 171 | select i).FirstOrDefault(); 172 | if (image != null) 173 | { 174 | if (!string.IsNullOrWhiteSpace(image.SavedFilename)) 175 | return true; 176 | } 177 | } 178 | return false; 179 | } 180 | 181 | public bool IsDoujin { get; set; } 182 | public bool IsVideo { get; set; } 183 | } 184 | } -------------------------------------------------------------------------------- /NijieDownloader.Library/Model/NijieLoginInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace NijieDownloader.Library.Model 7 | { 8 | public class NijieLoginInfo 9 | { 10 | public String UserName { get; set; } 11 | public String Password { get; set; } 12 | public String ReturnUrl { get; set; } 13 | public String Ticket { get; set; } 14 | public bool RememberLogin { get; set; } 15 | 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /NijieDownloader.Library/Model/NijieMangaInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.ComponentModel.DataAnnotations; 6 | using System.ComponentModel.DataAnnotations.Schema; 7 | 8 | namespace NijieDownloader.Library.Model 9 | { 10 | public class NijieMangaInfo 11 | { 12 | public NijieImage Image { get; set; } 13 | 14 | [Key, Column(Order = 0)] 15 | [DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None)] 16 | public int ImageId { get; set; } 17 | 18 | [Key, Column(Order = 1)] 19 | [DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None)] 20 | public int Page { get; set; } 21 | 22 | public string SavedFilename { get; set; } 23 | public string ServerFilename { get; set; } 24 | public long Filesize { get; set; } 25 | public string ImageUrl { get; set; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /NijieDownloader.Library/Model/NijieMember.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.ComponentModel.DataAnnotations.Schema; 4 | 5 | using Nandaka.Common; 6 | 7 | namespace NijieDownloader.Library.Model 8 | { 9 | public enum MemberMode 10 | { 11 | Images = 0, 12 | Doujin = 1, 13 | Bookmark = 2 14 | }; 15 | 16 | public class NijieMember 17 | { 18 | [Key] 19 | [DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None)] 20 | public int MemberId { get; set; } 21 | 22 | public string UserName { get; set; } 23 | 24 | public string AvatarUrl { get; set; } 25 | 26 | public virtual ICollection Images { get; set; } 27 | 28 | [NotMapped] 29 | public MemberMode Mode { get; set; } 30 | 31 | [NotMapped] 32 | public string MemberUrl 33 | { 34 | get 35 | { 36 | return GenerateMemberUrl(MemberId, Mode, Page); 37 | } 38 | private set { } 39 | } 40 | 41 | [NotMapped] 42 | public int Page { get; set; } 43 | 44 | [NotMapped] 45 | public string Status { get; set; } 46 | 47 | [NotMapped] 48 | public bool UseHttps { get; set; } 49 | 50 | public NijieMember() 51 | { 52 | this.MemberId = -1; 53 | } 54 | 55 | public NijieMember(int memberId, MemberMode mode, int page = 0) 56 | { 57 | this.MemberId = memberId; 58 | this.Mode = mode; 59 | this.Page = page; 60 | } 61 | 62 | public static string GenerateMemberUrl(int memberId, MemberMode mode, int page) 63 | { 64 | var prefix = ""; 65 | switch (mode) 66 | { 67 | case MemberMode.Images: 68 | prefix = "//nijie.info/members_illust.php?p=" + page + "&id="; 69 | break; 70 | 71 | case MemberMode.Doujin: 72 | prefix = "//nijie.info/members_dojin.php?id="; 73 | break; 74 | 75 | case MemberMode.Bookmark: 76 | prefix = "//nijie.info/user_like_illust_view.php?p=" + page + "&id="; 77 | break; 78 | } 79 | 80 | return Util.FixUrl(prefix + memberId, Nijie.ROOT_DOMAIN, Properties.Settings.Default.UseHttps); 81 | } 82 | 83 | [NotMapped] 84 | public bool IsNextAvailable { get; set; } 85 | 86 | [NotMapped] 87 | public int TotalImages { get; set; } 88 | 89 | } 90 | } -------------------------------------------------------------------------------- /NijieDownloader.Library/Model/NijieSearch.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | using Nandaka.Common; 7 | 8 | namespace NijieDownloader.Library.Model 9 | { 10 | public class NijieSearch 11 | { 12 | public NijieSearch(NijieSearchOption option) 13 | { 14 | this.Option = option; 15 | } 16 | 17 | public NijieSearchOption Option { get; set; } 18 | 19 | public string QueryUrl 20 | { 21 | get 22 | { 23 | return GenerateQueryUrl(Option); 24 | } 25 | private set { } 26 | } 27 | 28 | public List Images { get; set; } 29 | 30 | public bool IsNextAvailable { get; set; } 31 | 32 | public bool IsPrevAvailable 33 | { 34 | get 35 | { 36 | if (Option != null) 37 | return Option.Page > 1; 38 | return false; 39 | } 40 | } 41 | 42 | public static string GenerateQueryUrl(NijieSearchOption option) 43 | { 44 | var url = String.Format(@"//nijie.info/search.php?type={0}&word={1}&p={2}&mode={3}&illust_type={4}&sort={5}" 45 | , option.SearchTypeStr 46 | , option.Query 47 | , (int)option.Page 48 | , (int)option.SearchBy 49 | , "" 50 | , (int)option.Sort); 51 | return Util.FixUrl(url, Nijie.ROOT_DOMAIN, Properties.Settings.Default.UseHttps); 52 | } 53 | 54 | public int TotalImages { get; set; } 55 | } 56 | } -------------------------------------------------------------------------------- /NijieDownloader.Library/Model/NijieSearchOption.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace NijieDownloader.Library.Model 7 | { 8 | public class NijieSearchOption 9 | { 10 | public string Query { get; set; } 11 | 12 | private int _page = 1; 13 | public int Page 14 | { 15 | get 16 | { 17 | return _page; 18 | } 19 | set 20 | { 21 | _page = value; 22 | } 23 | } 24 | 25 | public SortType Sort { get; set; } 26 | public SearchMode SearchBy { get; set; } 27 | public SearchType Matching { get; set; } 28 | 29 | public string SearchTypeStr 30 | { 31 | get 32 | { 33 | return GetStringValue(this.Matching); 34 | } 35 | private set { } 36 | } 37 | 38 | private static string GetStringValue(SearchType type) 39 | { 40 | var result = ""; 41 | switch (type) 42 | { 43 | case SearchType.ExactMatch: 44 | result = "coincident"; 45 | break; 46 | case SearchType.PartialMatch: 47 | result = "partial"; 48 | break; 49 | default: 50 | result = "coincident"; 51 | break; 52 | } 53 | return result; 54 | } 55 | } 56 | 57 | public enum SortType 58 | { 59 | Latest = 0, 60 | Popularity = 1, 61 | Overtake = 2, 62 | Oldest = 3 63 | } 64 | 65 | public enum SearchMode 66 | { 67 | Tag = 0, 68 | Title = 1 69 | } 70 | 71 | public enum SearchType 72 | { 73 | ExactMatch, 74 | PartialMatch 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /NijieDownloader.Library/Model/NijieTag.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.ComponentModel.DataAnnotations; 6 | using System.ComponentModel.DataAnnotations.Schema; 7 | 8 | namespace NijieDownloader.Library.Model 9 | { 10 | public class NijieTag 11 | { 12 | [Key] 13 | [DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None)] 14 | public string Name { get; set; } 15 | public virtual ICollection Images { get; set; } 16 | 17 | public override string ToString() 18 | { 19 | return Name; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /NijieDownloader.Library/Nijie.Bookmark.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Text.RegularExpressions; 6 | using HtmlAgilityPack; 7 | using Nandaka.Common; 8 | using NijieDownloader.Library.Model; 9 | 10 | namespace NijieDownloader.Library 11 | { 12 | public partial class Nijie 13 | { 14 | private Regex re_member = new Regex(@"id=(\d+)"); 15 | public const string ROOT_DOMAIN = "nijie.info"; 16 | 17 | public Tuple, bool> ParseMyMemberBookmark(int page) 18 | { 19 | canOperate(); 20 | List members = new List(); 21 | 22 | HtmlDocument doc = null; 23 | bool isNextPageAvailable = false; 24 | try 25 | { 26 | var url = Util.FixUrl(String.Format("//nijie.info/like_my.php?p={0}", page), ROOT_DOMAIN, Properties.Settings.Default.UseHttps); 27 | var result = getPage(url); 28 | doc = result.Item1; 29 | 30 | var membersDiv = doc.DocumentNode.SelectNodes("//div[@class='nijie-okini']"); 31 | if (membersDiv != null) 32 | { 33 | foreach (var memberDiv in membersDiv) 34 | { 35 | var memberUrl = memberDiv.SelectSingleNode("//div[@class='nijie-okini']//a").Attributes["href"].Value; 36 | var res = re_member.Match(memberUrl); 37 | if (res.Success) 38 | { 39 | var member = new NijieMember(Int32.Parse(res.Groups[1].Value), 0); 40 | member.UserName = memberDiv.SelectSingleNode("//div[@class='nijie-okini']//p[@class='sougo']").InnerText; 41 | member.AvatarUrl = memberDiv.SelectSingleNode("//div[@class='nijie-okini']//a//img").Attributes["src"].Value; 42 | members.Add(member); 43 | } 44 | memberDiv.Remove(); 45 | } 46 | } 47 | 48 | var nextPageButton = doc.DocumentNode.SelectNodes("//p[@class='page_button']//a"); 49 | if (nextPageButton != null) 50 | { 51 | foreach (var item in nextPageButton) 52 | { 53 | if (item.InnerText.StartsWith("次へ")) 54 | { 55 | isNextPageAvailable = true; 56 | break; 57 | } 58 | } 59 | } 60 | } 61 | catch (Exception ex) 62 | { 63 | if (doc != null) 64 | { 65 | var filename = "Dump for My Member Bookmark.html"; 66 | Log.Debug("Dumping My Member Bookmark page to: " + filename); 67 | doc.Save(filename); 68 | } 69 | 70 | throw new NijieException(String.Format("Error when processing my member bookmark ==> {0}", ex.Message), ex, NijieException.IMAGE_UNKNOWN_ERROR); 71 | } 72 | 73 | return new Tuple, bool>(members, isNextPageAvailable); 74 | } 75 | 76 | public Tuple, bool> ParseMyImageBookmark(int page) 77 | { 78 | canOperate(); 79 | List images = new List(); 80 | 81 | HtmlDocument doc = null; 82 | bool isNextPageAvailable = false; 83 | try 84 | { 85 | var url = Util.FixUrl(String.Format("//nijie.info/okiniiri.php?p={0}", page), ROOT_DOMAIN, Properties.Settings.Default.UseHttps); 86 | var result = getPage(url); 87 | doc = result.Item1; 88 | 89 | var imagesDiv = doc.DocumentNode.SelectNodes("//div[@class='nijie-bookmark']"); 90 | if (imagesDiv != null) 91 | { 92 | foreach (var imageDivx in imagesDiv) 93 | { 94 | var tmp = imageDivx.InnerHtml; 95 | HtmlDocument imageDiv = new HtmlDocument(); 96 | imageDiv.LoadHtml(tmp); 97 | 98 | var imageUrl = imageDiv.DocumentNode.SelectSingleNode("//a").Attributes["href"].Value; 99 | var res = re_image.Match(imageUrl); 100 | if (res.Success) 101 | { 102 | var image = new NijieImage(Int32.Parse(res.Groups[1].Value)); 103 | image.Title = imageDiv.DocumentNode.SelectSingleNode("//p[@class='title']").InnerText; 104 | var tempThumb = imageDiv.DocumentNode.SelectSingleNode("//p[@class='nijiedao']//img").Attributes["src"].Value; 105 | image.ThumbImageUrl = Util.FixUrl(tempThumb, ROOT_DOMAIN, Properties.Settings.Default.UseHttps); 106 | 107 | // check if image is friend only 108 | // img src="//img.nijie.info/pic/common_icon/illust/friends.png" 109 | image.IsFriendOnly = false; 110 | if (image.ThumbImageUrl.EndsWith("friends.png")) 111 | { 112 | image.IsFriendOnly = true; 113 | } 114 | 115 | //"//img.nijie.info/pic/common_icon/illust/golden.png" 116 | image.IsGoldenMember = false; 117 | if (image.ThumbImageUrl.EndsWith("golden.png")) 118 | { 119 | image.IsGoldenMember = true; 120 | } 121 | 122 | // check manga icon 123 | image.IsManga = false; 124 | var icon = imageDiv.DocumentNode.SelectSingleNode("//div[@class='thumbnail-icon']/img"); 125 | if (icon != null) 126 | { 127 | if (icon.Attributes["src"].Value.EndsWith("thumbnail_comic.png")) 128 | image.IsManga = true; 129 | } 130 | 131 | // check animation icon 132 | image.IsAnimated = false; 133 | var animeIcon = imageDiv.DocumentNode.SelectSingleNode("//div[@class='thumbnail-anime-icon']/img"); 134 | if (animeIcon != null) 135 | { 136 | if (animeIcon.Attributes["src"].Value.EndsWith("thumbnail_anime.png")) 137 | image.IsAnimated = true; 138 | } 139 | 140 | images.Add(image); 141 | } 142 | imageDivx.Remove(); 143 | } 144 | } 145 | 146 | var nextPageButton = doc.DocumentNode.SelectNodes("//p[@class='page_button']//a"); 147 | if (nextPageButton != null) 148 | { 149 | foreach (var item in nextPageButton) 150 | { 151 | if (item.InnerText.StartsWith("次へ")) 152 | { 153 | isNextPageAvailable = true; 154 | break; 155 | } 156 | } 157 | } 158 | } 159 | catch (Exception ex) 160 | { 161 | if (doc != null) 162 | { 163 | var filename = "Dump for My Image Bookmark.html"; 164 | Log.Debug("Dumping My Image Bookmark page to: " + filename); 165 | doc.Save(filename); 166 | } 167 | 168 | throw new NijieException(String.Format("Error when processing my image bookmark ==> {0}", ex.Message), ex, NijieException.IMAGE_UNKNOWN_ERROR); 169 | } 170 | 171 | return new Tuple, bool>(images, isNextPageAvailable); 172 | } 173 | } 174 | } -------------------------------------------------------------------------------- /NijieDownloader.Library/Nijie.Login.cs: -------------------------------------------------------------------------------- 1 | using HtmlAgilityPack; 2 | using Nandaka.Common; 3 | using NijieDownloader.Library.Model; 4 | using System; 5 | using System.Collections.Specialized; 6 | using System.Diagnostics; 7 | using System.Net; 8 | using System.Threading.Tasks; 9 | 10 | namespace NijieDownloader.Library 11 | { 12 | public partial class Nijie 13 | { 14 | public static event NijieEventHandler LoggingEventHandler; 15 | 16 | public delegate void NijieEventHandler(object sender, bool result); 17 | 18 | private static string NijieSessionID { get; set; } 19 | 20 | private static bool _isLoggedIn; 21 | 22 | public static bool IsLoggedIn 23 | { 24 | get 25 | { 26 | return Nijie._isLoggedIn; 27 | } 28 | private set 29 | { 30 | Nijie._isLoggedIn = value; 31 | if (LoggingEventHandler != null) 32 | { 33 | LoggingEventHandler(null, value); 34 | } 35 | } 36 | } 37 | 38 | public bool Login(string userName, string password, bool retryLoop = false) 39 | { 40 | var info = PrepareLoginInfo(userName, password); 41 | return DoLogin(info, retryLoop); 42 | } 43 | 44 | public void Logout() 45 | { 46 | ExtendedWebClient.ClearCookie(); 47 | IsLoggedIn = false; 48 | } 49 | 50 | public void LoginAsync(string userName, string password, Action callback) 51 | { 52 | var task = Task.Factory.StartNew(() => Login(userName, password)); 53 | task.ContinueWith(x => 54 | { 55 | try 56 | { 57 | if (x.Result) 58 | callback(x.Result, "Login Success."); 59 | else 60 | callback(x.Result, "Invalid username or password or require age verification."); 61 | } 62 | catch (AggregateException ex) 63 | { 64 | callback(false, "Error: " + ex.InnerException.Message); 65 | } 66 | }, TaskScheduler.FromCurrentSynchronizationContext()); 67 | } 68 | 69 | private void PrintCookie(string header) 70 | { 71 | Debug.WriteLine(header); 72 | var uri = new Uri(Util.FixUrl("//nijie.info", ROOT_DOMAIN, Properties.Settings.Default.UseHttps)); 73 | foreach (Cookie item in ExtendedWebClient.CookieJar.GetCookies(uri)) 74 | { 75 | Debug.WriteLine("\tSite: {0} ==> {1}: {2}", item.Domain, item.Name, item.Value); 76 | } 77 | } 78 | 79 | private NijieLoginInfo PrepareLoginInfo(string userName, string password) 80 | { 81 | ExtendedWebClient client = new ExtendedWebClient(); 82 | 83 | // not really used 84 | var uri = new Uri(Util.FixUrl("//nijie.info", ROOT_DOMAIN, Properties.Settings.Default.UseHttps)); 85 | var tick = ((int)Util.DateTimeToUnixTimestamp(DateTime.Now)).ToString(); 86 | ExtendedWebClient.CookieJar.Add(uri, new Cookie("nijie_token_secret", tick)); 87 | ExtendedWebClient.CookieJar.Add(uri, new Cookie("nijie_token", tick)); 88 | 89 | NijieLoginInfo info = new NijieLoginInfo() { UserName = userName, Password = password, ReturnUrl = "", Ticket = "", RememberLogin = true }; 90 | 91 | HtmlDocument doc = getPage(Util.FixUrl(NijieConstants.NIJIE_LOGIN_URL, ROOT_DOMAIN, Properties.Settings.Default.UseHttps)).Item1; 92 | 93 | var tickets = doc.DocumentNode.SelectNodes("//input[@name='ticket']"); 94 | if (tickets != null && tickets.Count > 0) 95 | info.Ticket = tickets[0].Attributes["value"].Value; 96 | 97 | var returnUrls = doc.DocumentNode.SelectNodes("//input[@name='url']"); 98 | if (returnUrls != null && returnUrls.Count > 0) 99 | info.ReturnUrl = returnUrls[0].Attributes["value"].Value; 100 | 101 | PrintCookie("Prepare Login:"); 102 | 103 | return info; 104 | } 105 | 106 | private bool DoLogin(NijieLoginInfo info, bool retryLoop = false) 107 | { 108 | IsLoggedIn = false; 109 | ExtendedWebClient client = new ExtendedWebClient(); 110 | NameValueCollection loginInfo = new NameValueCollection(); 111 | loginInfo.Add("email", info.UserName); 112 | loginInfo.Add("password", info.Password); 113 | if (info.RememberLogin) 114 | loginInfo.Add("save", "on"); 115 | loginInfo.Add("ticket", info.Ticket); 116 | loginInfo.Add("url", info.ReturnUrl); 117 | 118 | var result = client.UploadValues(Util.FixUrl(NijieConstants.NIJIE_LOGIN_URL2, ROOT_DOMAIN, Properties.Settings.Default.UseHttps), "POST", loginInfo); 119 | //String data = Encoding.UTF8.GetString(result); 120 | 121 | var location = client.Response.ResponseUri.ToString(); 122 | if (!String.IsNullOrWhiteSpace(location)) 123 | { 124 | if (location.Contains(@"//nijie.info/login.php?")) 125 | { 126 | IsLoggedIn = false; 127 | } 128 | else if (location.Contains(@"//nijie.info/age_ver.php?")) 129 | { 130 | Debug.WriteLine("Please perform age verification"); 131 | IsLoggedIn = false; 132 | if (!retryLoop) 133 | { 134 | Login(info.UserName, info.Password, true); 135 | } 136 | } 137 | else 138 | { 139 | IsLoggedIn = true; 140 | } 141 | } 142 | 143 | var uri = new Uri(Util.FixUrl("//nijie.info", ROOT_DOMAIN, Properties.Settings.Default.UseHttps)); 144 | ExtendedWebClient.CookieJar.Add(uri, new Cookie("R18", "1")); 145 | var cookies = ExtendedWebClient.CookieJar.GetCookies(uri); 146 | foreach (Cookie item in cookies) 147 | { 148 | //Cookie: NIJIEIJIEID=lp1ffmjc9gi7a3u9qkj8p566u3 149 | if (item.Name == "NIJIEIJIEID") 150 | { 151 | NijieSessionID = item.Value; 152 | item.Expires = DateTime.MaxValue; 153 | break; 154 | } 155 | } 156 | 157 | PrintCookie("Login:"); 158 | 159 | return IsLoggedIn; 160 | } 161 | } 162 | } -------------------------------------------------------------------------------- /NijieDownloader.Library/NijieConstants.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace NijieDownloader.Library 7 | { 8 | public class NijieConstants 9 | { 10 | public const String NIJIE_URLx = "//nijie.info/"; 11 | public const String NIJIE_INDEXx = "//nijie.info/index.php"; 12 | 13 | public const String NIJIE_LOGIN_URL = "//nijie.info/login.php"; 14 | public const String NIJIE_LOGIN_URL2 = "//nijie.info/login_int.php"; 15 | 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /NijieDownloader.Library/NijieException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace NijieDownloader.Library 7 | { 8 | public class NijieException : Exception 9 | { 10 | public const int OK = 0; 11 | public const int CANCELLED = 1; 12 | public const int NOT_LOGGED_IN = 1000; 13 | public const int DOWNLOAD_ERROR = 1001; 14 | public const int INVALID_SAVE_FILENAME_FORMAT = 1002; 15 | public const int RENAME_ERROR = 1003; 16 | public const int DOWNLOAD_SKIPPED = 1004; 17 | public const int DB_ERROR = 1005; 18 | public const int DOWNLOAD_CANCELLED = 1006; 19 | public const int DOWNLOAD_UNKNOWN_ERROR = 1999; 20 | 21 | public const int MEMBER_REDIR = 2000; 22 | public const int MEMBER_UNKNOWN_ERROR = 2999; 23 | 24 | public const int IMAGE_UNKNOWN_ERROR = 3999; 25 | public const int IMAGE_NOT_FOUND = 3001; 26 | public const int IMAGE_BIG_PARSE_ERROR = 3002; 27 | 28 | public const int SEARCH_UNKNOWN_ERROR = 4999; 29 | 30 | #region ctor 31 | 32 | public NijieException(string message, int errorCode) 33 | : base(message) 34 | { 35 | this.ErrorCode = errorCode; 36 | } 37 | 38 | public NijieException(string message, Exception innerException, int errorCode) 39 | : base(message, innerException) 40 | { 41 | this.ErrorCode = errorCode; 42 | } 43 | 44 | #endregion ctor 45 | 46 | public int ErrorCode { get; set; } 47 | 48 | public int MemberId { get; set; } 49 | 50 | public int ImageId { get; set; } 51 | 52 | public string Query { get; set; } 53 | 54 | public override string ToString() 55 | { 56 | return string.Format("[{0}] {1}", ErrorCode, Message); 57 | } 58 | 59 | public string Url { get; set; } 60 | 61 | public string Filename { get; set; } 62 | } 63 | } -------------------------------------------------------------------------------- /NijieDownloader.Library/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("NijieDownloader.Library")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("NijieDownloader.Library")] 13 | [assembly: AssemblyCopyright("Copyright © Nandaka 2014-2021")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("16ddf404-4554-480a-93c2-af1b36d416ee")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.9.6")] 36 | [assembly: AssemblyFileVersion("1.0.9.6")] 37 | -------------------------------------------------------------------------------- /NijieDownloader.Library/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace NijieDownloader.Library.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.9.0.0")] 16 | public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | 26 | [global::System.Configuration.UserScopedSettingAttribute()] 27 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 28 | [global::System.Configuration.DefaultSettingValueAttribute("True")] 29 | public bool UseHttps { 30 | get { 31 | return ((bool)(this["UseHttps"])); 32 | } 33 | set { 34 | this["UseHttps"] = value; 35 | } 36 | } 37 | 38 | [global::System.Configuration.UserScopedSettingAttribute()] 39 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 40 | [global::System.Configuration.DefaultSettingValueAttribute("3")] 41 | public int RetryCount { 42 | get { 43 | return ((int)(this["RetryCount"])); 44 | } 45 | set { 46 | this["RetryCount"] = value; 47 | } 48 | } 49 | 50 | [global::System.Configuration.UserScopedSettingAttribute()] 51 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 52 | [global::System.Configuration.DefaultSettingValueAttribute("False")] 53 | public bool MakeBackup { 54 | get { 55 | return ((bool)(this["MakeBackup"])); 56 | } 57 | set { 58 | this["MakeBackup"] = value; 59 | } 60 | } 61 | 62 | [global::System.Configuration.UserScopedSettingAttribute()] 63 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 64 | [global::System.Configuration.DefaultSettingValueAttribute("False")] 65 | public bool Overwrite { 66 | get { 67 | return ((bool)(this["Overwrite"])); 68 | } 69 | set { 70 | this["Overwrite"] = value; 71 | } 72 | } 73 | 74 | [global::System.Configuration.UserScopedSettingAttribute()] 75 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 76 | [global::System.Configuration.DefaultSettingValueAttribute("False")] 77 | public bool OverwriteOnlyIfDifferentSize { 78 | get { 79 | return ((bool)(this["OverwriteOnlyIfDifferentSize"])); 80 | } 81 | set { 82 | this["OverwriteOnlyIfDifferentSize"] = value; 83 | } 84 | } 85 | 86 | [global::System.Configuration.UserScopedSettingAttribute()] 87 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 88 | [global::System.Configuration.DefaultSettingValueAttribute("60")] 89 | public int RetryDelay { 90 | get { 91 | return ((int)(this["RetryDelay"])); 92 | } 93 | set { 94 | this["RetryDelay"] = value; 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /NijieDownloader.Library/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | True 7 | 8 | 9 | 3 10 | 11 | 12 | False 13 | 14 | 15 | False 16 | 17 | 18 | False 19 | 20 | 21 | 60 22 | 23 | 24 | -------------------------------------------------------------------------------- /NijieDownloader.Library/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /NijieDownloader.UI/App.xaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 11 | 12 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 36 | 41 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /NijieDownloader.UI/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Configuration; 5 | using System.Data; 6 | using System.Linq; 7 | using System.Windows; 8 | 9 | namespace NijieDownloader.UI 10 | { 11 | /// 12 | /// Interaction logic for App.xaml 13 | /// 14 | public partial class App : Application 15 | { 16 | } 17 | } -------------------------------------------------------------------------------- /NijieDownloader.UI/ImageLoader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Specialized; 3 | using System.IO; 4 | using System.Runtime.Caching; 5 | using System.Threading.Tasks; 6 | using System.Windows.Media.Imaging; 7 | using Nandaka.Common; 8 | using NijieDownloader.Library; 9 | using NijieDownloader.UI.ViewModel; 10 | 11 | namespace NijieDownloader.UI 12 | { 13 | public class ImageLoader 14 | { 15 | private static ObjectCache cache; 16 | private static TaskFactory _imageFactory; 17 | 18 | public const string IMAGE_LOADING = "Loading"; 19 | public const string IMAGE_LOADED = "Done"; 20 | public const string IMAGE_ERROR = "Error"; 21 | public const string IMAGE_QUEUED = "Queued"; 22 | 23 | /// 24 | /// Init the ImageLoader, changes on these settings require app restart: 25 | /// - ConcurrentImageLoad 26 | /// - cacheMemoryLimitMegabytes 27 | /// 28 | static ImageLoader() 29 | { 30 | configureCache(); 31 | _imageFactory = new TaskFactory(new LimitedConcurrencyLevelTaskScheduler(Properties.Settings.Default.ConcurrentImageLoad, 8)); 32 | } 33 | 34 | private static void configureCache() 35 | { 36 | var config = new NameValueCollection(); 37 | config.Add("pollingInterval", "00:05:00"); 38 | config.Add("physicalMemoryLimitPercentage", "0"); 39 | config.Add("cacheMemoryLimitMegabytes", Properties.Settings.Default.cacheMemoryLimitMegabytes); 40 | cache = new MemoryCache("CustomCache", config); 41 | } 42 | 43 | /// 44 | /// Load image from cache if available, else start new task to download the image. 45 | /// 46 | /// 47 | /// 48 | /// 49 | public static void LoadImage(string url, string referer, Action action) 50 | { 51 | if (String.IsNullOrWhiteSpace(url)) return; 52 | url = Util.FixUrl(url, Nijie.ROOT_DOMAIN); 53 | referer = Util.FixUrl(referer, Nijie.ROOT_DOMAIN); 54 | 55 | if (!cache.Contains(url)) 56 | { 57 | _imageFactory.StartNew(() => 58 | { 59 | try 60 | { 61 | action(ViewModelHelper.Loading, IMAGE_LOADING); 62 | 63 | MainWindow.Log.Debug("Loading image: " + url); 64 | #if DEBUG 65 | var result = File.ReadAllBytes(@"../../Resources/error_icon.png"); 66 | #else 67 | var result = MainWindow.Bot.DownloadData(url, referer); 68 | #endif 69 | using (var ms = new MemoryStream(result)) 70 | { 71 | var t = new BitmapImage(); 72 | t.BeginInit(); 73 | t.CacheOption = BitmapCacheOption.OnLoad; 74 | t.StreamSource = ms; 75 | t.EndInit(); 76 | t.Freeze(); 77 | action(t, IMAGE_LOADED); 78 | 79 | CacheItemPolicy policy = new CacheItemPolicy(); 80 | policy.SlidingExpiration = new TimeSpan(1, 0, 0); 81 | 82 | cache.Set(url, t, policy); 83 | } 84 | } 85 | catch (Exception ex) 86 | { 87 | action(null, IMAGE_ERROR); 88 | 89 | MainWindow.Log.Error(String.Format("Error when loading image: {0}", ex.Message), ex); 90 | if (ex.InnerException != null) 91 | MainWindow.Log.Error(ex.InnerException.Message, ex.InnerException); 92 | } 93 | }); 94 | } 95 | else 96 | { 97 | MainWindow.Log.Debug("Loaded from cache: " + url); 98 | action((BitmapImage)cache.Get(url), IMAGE_LOADED); 99 | } 100 | } 101 | } 102 | } -------------------------------------------------------------------------------- /NijieDownloader.UI/JobStatusToVisibilityConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Windows.Data; 6 | using NijieDownloader.UI.ViewModel; 7 | using System.Windows; 8 | 9 | namespace NijieDownloader.UI 10 | { 11 | public class JobStatusToVisibilityConverter : IValueConverter 12 | { 13 | public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 14 | { 15 | JobStatus val = (JobStatus)value; 16 | 17 | if (targetType == typeof(Visibility)) 18 | { 19 | if (val == JobStatus.Running || val == JobStatus.Canceling) 20 | return Visibility.Visible; 21 | else 22 | return Visibility.Collapsed; 23 | } 24 | return Visibility.Collapsed; 25 | } 26 | 27 | public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 28 | { 29 | throw new NotImplementedException(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /NijieDownloader.UI/JobTypeToVisibilityConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Windows.Data; 6 | using NijieDownloader.UI.ViewModel; 7 | using System.Windows; 8 | 9 | namespace NijieDownloader.UI 10 | { 11 | public class JobTypeToVisibilityConverter: IValueConverter 12 | { 13 | public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 14 | { 15 | JobType val = (JobType)value; 16 | 17 | if (targetType == typeof(Visibility)) 18 | { 19 | if (val.ToString() == parameter.ToString()) 20 | return Visibility.Visible; 21 | else 22 | return Visibility.Collapsed; 23 | } 24 | return Visibility.Collapsed; 25 | } 26 | 27 | public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 28 | { 29 | throw new NotImplementedException(); 30 | } 31 | } 32 | 33 | public class IsMangaToVisibilityConverter : IValueConverter 34 | { 35 | public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 36 | { 37 | bool isManga = (bool)value; 38 | 39 | if (targetType == typeof(Visibility)) 40 | { 41 | if (isManga) 42 | return Visibility.Visible; 43 | else 44 | return Visibility.Collapsed; 45 | } 46 | return Visibility.Collapsed; 47 | } 48 | 49 | public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 50 | { 51 | throw new NotImplementedException(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /NijieDownloader.UI/Main/BookmarkPage.xaml: -------------------------------------------------------------------------------- 1 |  12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 22 | 25 | 28 | 31 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 43 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /NijieDownloader.UI/Settings/About.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Reflection; 3 | using System.Windows.Controls; 4 | 5 | namespace NijieDownloader.UI.Settings 6 | { 7 | /// 8 | /// Interaction logic for About.xaml 9 | /// 10 | public partial class About : UserControl 11 | { 12 | public About() 13 | { 14 | InitializeComponent(); 15 | 16 | if (System.Deployment.Application.ApplicationDeployment.IsNetworkDeployed) 17 | { 18 | System.Deployment.Application.ApplicationDeployment ad = System.Deployment.Application.ApplicationDeployment.CurrentDeployment; 19 | txtVersion.Text = ad.CurrentVersion.ToString(); 20 | } 21 | else 22 | { 23 | Assembly assembly = Assembly.GetExecutingAssembly(); 24 | FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(assembly.Location); 25 | txtVersion.Text = fvi.ProductVersion; 26 | } 27 | } 28 | 29 | private void btnDonate_Click(object sender, System.Windows.RoutedEventArgs e) 30 | { 31 | var uri = new System.Uri(@"https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=Nchek2000%40gmail%2ecom&lc=US&item_name=NijieDownloader¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted"); 32 | Process.Start(new ProcessStartInfo(uri.ToString())); 33 | e.Handled = true; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /NijieDownloader.UI/Settings/LoginInfo.xaml: -------------------------------------------------------------------------------- 1 |  13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | Auto Login 40 | Use Https 42 | 43 | 44 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /NijieDownloader.UI/Settings/LoginInfo.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 | using FirstFloor.ModernUI.Windows; 16 | using FirstFloor.ModernUI.Windows.Controls; 17 | using FirstFloor.ModernUI.Windows.Navigation; 18 | using Nandaka.Common; 19 | using NijieDownloader.Library; 20 | using NijieDownloader.Library.Properties; 21 | 22 | namespace NijieDownloader.UI 23 | { 24 | /// 25 | /// Interaction logic for LoginInfo.xaml 26 | /// 27 | public partial class LoginInfo : Page, IContent 28 | { 29 | private ModernDialog dialog; 30 | private bool isAutoLogin = false; 31 | 32 | public List StartPage { get; set; } 33 | 34 | public LoginInfo() 35 | { 36 | InitializeComponent(); 37 | StartPage = new List(new string[] { "Member", "Image", "Search", "Bookmark", "BatchDownload" }); 38 | cbxStartPage.DataContext = this; 39 | 40 | txtUserName.Text = Properties.Settings.Default.Username; 41 | 42 | using (var secureString = Properties.Settings.Default.Password.DecryptString()) 43 | { 44 | txtPassword.Password = secureString.ToInsecureString(); 45 | } 46 | 47 | this.Loaded += new RoutedEventHandler(LoginInfo_Loaded); 48 | } 49 | 50 | private void LoginInfo_Loaded(object sender, RoutedEventArgs e) 51 | { 52 | if (isAutoLogin) 53 | { 54 | if (!Nijie.IsLoggedIn && Properties.Settings.Default.AutoLogin) 55 | { 56 | btnLogin_Click(this, null); 57 | isAutoLogin = false; 58 | } 59 | } 60 | } 61 | 62 | private void updateLoginStatus(bool result, string message) 63 | { 64 | lblLoginStatus.Text = message; 65 | if (result) 66 | { 67 | Properties.Settings.Default.Username = txtUserName.Text; 68 | 69 | using (var secureString = txtPassword.Password.ToSecureString()) 70 | { 71 | Properties.Settings.Default.Password = secureString.EncryptString(); 72 | } 73 | 74 | MainWindow.SaveAllSettings(); 75 | 76 | dialog.Close(); 77 | 78 | var uri = new Uri("/Main/" + Properties.Settings.Default.StartPage + "Page.xaml", UriKind.RelativeOrAbsolute); 79 | var frame = NavigationHelper.FindFrame(null, this); 80 | if (frame != null) 81 | { 82 | frame.Source = uri; 83 | } 84 | } 85 | dialog.Close(); 86 | } 87 | 88 | private void btnLogin_Click(object sender, RoutedEventArgs e) 89 | { 90 | if (!string.IsNullOrWhiteSpace(txtUserName.Text) && !string.IsNullOrWhiteSpace(txtPassword.Password)) 91 | { 92 | lblLoginStatus.Text = "Logging in..."; 93 | MainWindow.Bot.LoginAsync(txtUserName.Text, txtPassword.Password, updateLoginStatus); 94 | dialog = new ModernDialog(); 95 | dialog.Content = "Logging in..."; 96 | dialog.CloseButton.Visibility = System.Windows.Visibility.Hidden; 97 | dialog.ShowDialog(); 98 | } 99 | } 100 | 101 | public void OnFragmentNavigation(FirstFloor.ModernUI.Windows.Navigation.FragmentNavigationEventArgs e) 102 | { 103 | } 104 | 105 | public void OnNavigatedFrom(FirstFloor.ModernUI.Windows.Navigation.NavigationEventArgs e) 106 | { 107 | } 108 | 109 | public void OnNavigatedTo(FirstFloor.ModernUI.Windows.Navigation.NavigationEventArgs e) 110 | { 111 | if (e.Source.ToString().Contains("from_title_links=1")) 112 | { 113 | if (Nijie.IsLoggedIn) 114 | { 115 | var result = ModernDialog.ShowMessage("Logout?", "Confimation", MessageBoxButton.YesNo); 116 | if (result == MessageBoxResult.Yes) 117 | { 118 | MainWindow.Bot.Logout(); 119 | lblLoginStatus.Text = "Logged Out."; 120 | } 121 | } 122 | } 123 | else if (e.Source.ToString().Contains("autologin")) 124 | { 125 | isAutoLogin = true; 126 | } 127 | } 128 | 129 | public void OnNavigatingFrom(FirstFloor.ModernUI.Windows.Navigation.NavigatingCancelEventArgs e) 130 | { 131 | } 132 | 133 | private void CheckBox_Click(object sender, RoutedEventArgs e) 134 | { 135 | MainWindow.SaveAllSettings(); 136 | } 137 | } 138 | } -------------------------------------------------------------------------------- /NijieDownloader.UI/Settings/Network.xaml: -------------------------------------------------------------------------------- 1 |  11 | 12 | 13 | 14 | 15 | 16 | 17 | 19 | 20 | 21 | 22 | 24 | 25 | 27 | 29 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /NijieDownloader.UI/Settings/Network.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 | using Nandaka.Common; 17 | 18 | namespace NijieDownloader.UI.Settings 19 | { 20 | /// 21 | /// Interaction logic for Network.xaml 22 | /// 23 | public partial class Network : UserControl 24 | { 25 | public Network() 26 | { 27 | InitializeComponent(); 28 | } 29 | 30 | private void btnSave_Click(object sender, RoutedEventArgs e) 31 | { 32 | MainWindow.SaveAllSettings(); 33 | var proxy = ExtendedWebClient.GlobalProxy; 34 | if (proxy != null) proxy.ToString(); 35 | 36 | ExtendedWebClient.EnableCompression = Properties.Settings.Default.EnableCompression; 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /NijieDownloader.UI/ViewModel/JobDownloadViewModelComparer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace NijieDownloader.UI.ViewModel 7 | { 8 | public class JobDownloadViewModelComparer : IComparer, IEqualityComparer 9 | { 10 | public int Compare(JobDownloadViewModel x, JobDownloadViewModel other) 11 | { 12 | if (x.JobType == other.JobType) 13 | { 14 | switch (x.JobType) 15 | { 16 | case JobType.Image: 17 | if (x.ImageId == other.ImageId) 18 | return 0; 19 | break; 20 | 21 | case JobType.Member: 22 | if (x.MemberId == other.MemberId && 23 | x.MemberMode == other.MemberMode && 24 | //x.EndPage == other.EndPage && 25 | x.Limit == other.Limit) 26 | return 0; 27 | break; 28 | 29 | case JobType.Tags: 30 | if (x.SearchTag == other.SearchTag && 31 | x.SearchBy == other.SearchBy && 32 | x.Sort == other.Sort && 33 | x.Matching == other.Matching && 34 | x.StartPage == other.StartPage && 35 | x.EndPage == other.EndPage && 36 | x.Limit == other.Limit) 37 | return 0; 38 | break; 39 | 40 | default: 41 | return 1; 42 | } 43 | } 44 | return 1; 45 | } 46 | 47 | public bool Equals(JobDownloadViewModel x, JobDownloadViewModel y) 48 | { 49 | return Compare(x, y) == 0 ? true : false; 50 | } 51 | 52 | public int GetHashCode(JobDownloadViewModel obj) 53 | { 54 | return obj.GetHashCode(); 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /NijieDownloader.UI/ViewModel/NijieMemberBookmarkViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading; 7 | using NijieDownloader.Library; 8 | using NijieDownloader.Library.Model; 9 | 10 | namespace NijieDownloader.UI.ViewModel 11 | { 12 | public class NijieMemberBookmarkViewModel : ViewModelBase 13 | { 14 | private BookmarkType _bookmarkType; 15 | 16 | public BookmarkType BookmarkType 17 | { 18 | get 19 | { 20 | return _bookmarkType; 21 | } 22 | set 23 | { 24 | _bookmarkType = value; 25 | onPropertyChanged("BookmarkType"); 26 | } 27 | } 28 | 29 | private ObservableCollection _members; 30 | 31 | public ObservableCollection Members 32 | { 33 | get 34 | { 35 | return _members; 36 | } 37 | set 38 | { 39 | _members = value; 40 | onPropertyChanged("Members"); 41 | } 42 | } 43 | 44 | private ObservableCollection _images; 45 | 46 | public ObservableCollection Images 47 | { 48 | get 49 | { 50 | return _images; 51 | } 52 | set 53 | { 54 | _images = value; 55 | onPropertyChanged("Images"); 56 | } 57 | } 58 | 59 | private int _page = 1; 60 | 61 | public int Page 62 | { 63 | get 64 | { 65 | return _page; 66 | } 67 | set 68 | { 69 | _page = value; 70 | onPropertyChanged("Page"); 71 | } 72 | } 73 | 74 | private string _status; 75 | 76 | public string Status 77 | { 78 | get 79 | { 80 | return _status; 81 | } 82 | set 83 | { 84 | _status = value; 85 | onPropertyChanged("Status"); 86 | } 87 | } 88 | 89 | private bool _isNextPageAvailable; 90 | 91 | public bool IsNextPageAvailable 92 | { 93 | get 94 | { 95 | return _isNextPageAvailable; 96 | } 97 | set 98 | { 99 | _isNextPageAvailable = value; 100 | onPropertyChanged("IsNextPageAvailable"); 101 | } 102 | } 103 | 104 | public void GetMyMemberBookmark(SynchronizationContext context) 105 | { 106 | try 107 | { 108 | var result = MainWindow.Bot.ParseMyMemberBookmark(this.Page); 109 | 110 | this.Members = new ObservableCollection(); 111 | foreach (var item in result.Item1) 112 | { 113 | NijieMemberViewModel m = new NijieMemberViewModel(item); 114 | context.Send((x) => 115 | { 116 | this.Members.Add(m); 117 | }, null); 118 | } 119 | this.IsNextPageAvailable = result.Item2; 120 | this.Status = String.Format("Found {0} member(s).", this.Members.Count); 121 | } 122 | catch (NijieException ne) 123 | { 124 | this.Status = "Error: " + ne.Message; 125 | } 126 | } 127 | 128 | public void GetMyImagesBookmark(SynchronizationContext context) 129 | { 130 | try 131 | { 132 | var result = MainWindow.Bot.ParseMyImageBookmark(this.Page); 133 | 134 | this.Images = new ObservableCollection(); 135 | foreach (var item in result.Item1) 136 | { 137 | NijieImageViewModel m = new NijieImageViewModel(item); 138 | context.Send((x) => 139 | { 140 | this.Images.Add(m); 141 | }, null); 142 | } 143 | this.IsNextPageAvailable = result.Item2; 144 | this.Status = String.Format("Found {0} images(s).", this.Images.Count); 145 | } 146 | catch (NijieException ne) 147 | { 148 | this.Status = "Error: " + ne.Message; 149 | } 150 | } 151 | } 152 | 153 | public enum BookmarkType 154 | { 155 | Member, Image 156 | } 157 | } -------------------------------------------------------------------------------- /NijieDownloader.UI/ViewModel/NijieMemberViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.ComponentModel; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading; 8 | using System.Windows.Media.Imaging; 9 | using NijieDownloader.Library; 10 | using NijieDownloader.Library.Model; 11 | 12 | namespace NijieDownloader.UI.ViewModel 13 | { 14 | public class NijieMemberViewModel : ViewModelBase 15 | { 16 | private NijieMember _member; 17 | 18 | #region ctor 19 | 20 | public NijieMemberViewModel() 21 | { 22 | } 23 | 24 | public NijieMemberViewModel(NijieMember member) 25 | { 26 | _member = member; 27 | this.MemberId = member.MemberId; 28 | this.Mode = (MemberMode)member.Mode; 29 | } 30 | 31 | #endregion ctor 32 | 33 | #region properties 34 | 35 | private int _memberId; 36 | 37 | public int MemberId 38 | { 39 | get 40 | { 41 | return _memberId; 42 | } 43 | set 44 | { 45 | _memberId = value; 46 | onPropertyChanged("MemberId"); 47 | onPropertyChanged("MemberUrl"); 48 | } 49 | } 50 | 51 | private string _avatarImageStatus; 52 | private BitmapImage _avatarImage; 53 | 54 | public BitmapImage AvatarImage 55 | { 56 | get 57 | { 58 | if (_avatarImage == null) 59 | { 60 | var loading = ViewModelHelper.NoAvatar; 61 | if (_member != null && _avatarImageStatus != ImageLoader.IMAGE_LOADING) 62 | { 63 | loading = ViewModelHelper.Loading; 64 | _avatarImageStatus = ImageLoader.IMAGE_LOADING; 65 | ImageLoader.LoadImage(_member.AvatarUrl, _member.MemberUrl, 66 | new Action((image, status) => 67 | { 68 | this.AvatarImage = null; 69 | this.AvatarImage = image; 70 | _avatarImageStatus = status; 71 | } 72 | )); 73 | } 74 | return loading; 75 | } 76 | return _avatarImage; 77 | } 78 | set 79 | { 80 | _avatarImage = value; 81 | onPropertyChanged("AvatarImage"); 82 | } 83 | } 84 | 85 | private MemberMode _mode; 86 | 87 | public MemberMode Mode 88 | { 89 | get 90 | { 91 | return _mode; 92 | } 93 | set 94 | { 95 | _mode = value; 96 | if (_member != null) 97 | _member.Mode = value; 98 | onPropertyChanged("Mode"); 99 | onPropertyChanged("MemberUrl"); 100 | } 101 | } 102 | 103 | private int _page = 1; 104 | 105 | public int Page 106 | { 107 | get { return _page; } 108 | set 109 | { 110 | _page = value; 111 | onPropertyChanged("Page"); 112 | onPropertyChanged("MemberUrl"); 113 | } 114 | } 115 | 116 | public bool IsNextPageAvailable 117 | { 118 | get 119 | { 120 | if (_member != null) return _member.IsNextAvailable; 121 | return false; 122 | } 123 | } 124 | 125 | private ObservableCollection _images; 126 | 127 | public ObservableCollection Images 128 | { 129 | get 130 | { 131 | return _images; 132 | } 133 | set 134 | { 135 | _images = value; 136 | onPropertyChanged("Images"); 137 | } 138 | } 139 | 140 | private string _status; 141 | 142 | public string Status 143 | { 144 | get { return _status; } 145 | set 146 | { 147 | _status = value; 148 | onPropertyChanged("Status"); 149 | } 150 | } 151 | 152 | private string _username; 153 | public string UserName 154 | { 155 | get 156 | { 157 | return _username; 158 | } 159 | set 160 | { 161 | _username = value; 162 | onPropertyChanged("UserName"); 163 | } 164 | } 165 | 166 | public string MemberUrl 167 | { 168 | get 169 | { 170 | //if (_member != null) return _member.MemberUrl; 171 | return NijieMember.GenerateMemberUrl(MemberId, Mode, Page); 172 | } 173 | } 174 | 175 | private bool _isSelected; 176 | 177 | public bool IsSelected 178 | { 179 | get 180 | { 181 | return _isSelected; 182 | } 183 | set 184 | { 185 | _isSelected = value; 186 | onPropertyChanged("IsSelected"); 187 | } 188 | } 189 | 190 | public int TotalImages 191 | { 192 | get 193 | { 194 | if (_member != null) 195 | { 196 | return _member.TotalImages; 197 | } 198 | return -1; 199 | } 200 | } 201 | 202 | #endregion properties 203 | 204 | public void GetMember(SynchronizationContext context) 205 | { 206 | try 207 | { 208 | _member = MainWindow.Bot.ParseMember(this.MemberId, this.Mode, this.Page); 209 | this.UserName = _member.UserName; 210 | 211 | ImageLoader.LoadImage(_member.AvatarUrl, _member.MemberUrl, 212 | new Action((image, status) => 213 | { 214 | this.AvatarImage = null; 215 | this.AvatarImage = image; 216 | _avatarImageStatus = status; 217 | } 218 | )); 219 | 220 | if (_member.Images != null) 221 | { 222 | Images = new ObservableCollection(); 223 | foreach (var image in _member.Images) 224 | { 225 | var temp = new NijieImageViewModel(image); 226 | context.Send((x) => 227 | { 228 | Images.Add(temp); 229 | }, null); 230 | } 231 | 232 | this.Status = String.Format("Loaded: {0} images.", _member.Images.Count); 233 | onPropertyChanged("TotalImages"); 234 | this.HasError = false; 235 | } 236 | } 237 | catch (NijieException ne) 238 | { 239 | MainWindow.Log.Error(ne.Message, ne); 240 | 241 | this.UserName = null; 242 | this.AvatarImage = ViewModelHelper.NoAvatar; 243 | context.Send((x) => 244 | { 245 | if (Images != null) 246 | { 247 | Images.Clear(); 248 | Images = null; 249 | } 250 | }, null); 251 | 252 | this.HasError = true; 253 | this.Status = "[Error] " + ne.Message; 254 | } 255 | } 256 | } 257 | } -------------------------------------------------------------------------------- /NijieDownloader.UI/ViewModel/NijieSearchViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.ComponentModel; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading; 8 | using System.Windows.Media.Imaging; 9 | using NijieDownloader.Library; 10 | using NijieDownloader.Library.Model; 11 | 12 | namespace NijieDownloader.UI.ViewModel 13 | { 14 | public class NijieSearchViewModel : ViewModelBase 15 | { 16 | private NijieSearch _search; 17 | 18 | #region ctor 19 | 20 | public NijieSearchViewModel() 21 | { 22 | } 23 | 24 | #endregion ctor 25 | 26 | #region properties 27 | 28 | private SortType _sortType; 29 | 30 | public SortType Sort 31 | { 32 | get { return _sortType; } 33 | set 34 | { 35 | if (value != _sortType) 36 | { 37 | Page = 1; 38 | } 39 | _sortType = value; 40 | onPropertyChanged("Sort"); 41 | onPropertyChanged("QueryUrl"); 42 | } 43 | } 44 | 45 | private string _query; 46 | 47 | public string Query 48 | { 49 | get { return _query; } 50 | set 51 | { 52 | if (value != _query) 53 | { 54 | Page = 1; 55 | } 56 | _query = value; 57 | onPropertyChanged("Query"); 58 | onPropertyChanged("QueryUrl"); 59 | } 60 | } 61 | 62 | private int _page = 1; 63 | 64 | public int Page 65 | { 66 | get { return _page; } 67 | set 68 | { 69 | _page = value; 70 | onPropertyChanged("Page"); 71 | onPropertyChanged("QueryUrl"); 72 | } 73 | } 74 | 75 | private SearchMode _searchMode; 76 | 77 | public SearchMode SearchBy 78 | { 79 | get { return _searchMode; } 80 | set 81 | { 82 | if (value != _searchMode) 83 | { 84 | Page = 1; 85 | } 86 | _searchMode = value; 87 | onPropertyChanged("SearchBy"); 88 | onPropertyChanged("QueryUrl"); 89 | } 90 | } 91 | 92 | private SearchType _searchType; 93 | 94 | public SearchType Matching 95 | { 96 | get { return _searchType; } 97 | set 98 | { 99 | if (value != _searchType) 100 | { 101 | Page = 1; 102 | } 103 | _searchType = value; 104 | onPropertyChanged("Matching"); 105 | onPropertyChanged("QueryUrl"); 106 | } 107 | } 108 | 109 | private string _status; 110 | 111 | public string Status 112 | { 113 | get 114 | { return _status; } 115 | set 116 | { 117 | _status = value; 118 | onPropertyChanged("Status"); 119 | } 120 | } 121 | 122 | private ObservableCollection _images; 123 | 124 | public ObservableCollection Images 125 | { 126 | get 127 | { 128 | return _images; 129 | } 130 | set 131 | { 132 | _images = value; 133 | onPropertyChanged("Images"); 134 | } 135 | } 136 | 137 | public bool IsNextPageAvailable 138 | { 139 | get 140 | { 141 | if (_search != null) return _search.IsNextAvailable; 142 | return false; 143 | } 144 | } 145 | 146 | public string QueryUrl 147 | { 148 | get 149 | { 150 | if (_search != null) return _search.QueryUrl; 151 | 152 | NijieSearchOption option = new NijieSearchOption(); 153 | option.Sort = Sort; 154 | option.Query = Query; 155 | option.Page = Page; 156 | option.SearchBy = SearchBy; 157 | option.Matching = Matching; 158 | return NijieSearch.GenerateQueryUrl(option); 159 | } 160 | } 161 | 162 | public int TotalImages 163 | { 164 | get 165 | { 166 | if (_search != null) 167 | return _search.TotalImages; 168 | return -1; 169 | } 170 | } 171 | 172 | #endregion properties 173 | 174 | public void DoSearch(SynchronizationContext context) 175 | { 176 | NijieSearchOption option = new NijieSearchOption(); 177 | option.Sort = Sort; 178 | option.Query = Query; 179 | option.Page = Page; 180 | option.SearchBy = SearchBy; 181 | option.Matching = Matching; 182 | 183 | try 184 | { 185 | _search = MainWindow.Bot.Search(option); 186 | 187 | Images = new ObservableCollection(); 188 | foreach (var image in _search.Images) 189 | { 190 | var temp = new NijieImageViewModel(image); 191 | context.Send((x) => 192 | { 193 | Images.Add(temp); 194 | }, null); 195 | } 196 | 197 | onPropertyChanged("TotalImages"); 198 | } 199 | catch (NijieException ne) 200 | { 201 | Status = "Error: " + ne.Message; 202 | } 203 | } 204 | } 205 | } -------------------------------------------------------------------------------- /NijieDownloader.UI/ViewModel/ViewModelBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.ComponentModel; 6 | 7 | namespace NijieDownloader.UI.ViewModel 8 | { 9 | public class ViewModelBase : INotifyPropertyChanged 10 | { 11 | public event PropertyChangedEventHandler PropertyChanged; 12 | 13 | protected void onPropertyChanged(string name) 14 | { 15 | if (PropertyChanged != null) 16 | { 17 | PropertyChanged(this, new PropertyChangedEventArgs(name)); 18 | } 19 | } 20 | 21 | private bool _hasError; 22 | 23 | public bool HasError 24 | { 25 | get 26 | { 27 | return _hasError; 28 | } 29 | set 30 | { 31 | _hasError = value; 32 | onPropertyChanged("HasError"); 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /NijieDownloader.UI/ViewModel/ViewModelHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Windows.Media.Imaging; 6 | 7 | namespace NijieDownloader.UI.ViewModel 8 | { 9 | public class ViewModelHelper 10 | { 11 | private static BitmapImage _loading; 12 | public static BitmapImage Loading 13 | { 14 | get 15 | { 16 | if (_loading == null) 17 | { 18 | _loading = new BitmapImage(new Uri("pack://application:,,,/Resources/loading.png")); 19 | _loading.Freeze(); 20 | } 21 | return _loading; 22 | } 23 | private set { } 24 | } 25 | 26 | private static BitmapImage _friendOnly; 27 | public static BitmapImage FriendOnly 28 | { 29 | get 30 | { 31 | if (_friendOnly == null) 32 | { 33 | _friendOnly = new BitmapImage(new Uri("pack://application:,,,/Resources/friends.png")); 34 | _friendOnly.Freeze(); 35 | } 36 | return _friendOnly; 37 | } 38 | private set { } 39 | } 40 | 41 | private static BitmapImage _error; 42 | public static BitmapImage Error 43 | { 44 | get 45 | { 46 | if (_error == null) 47 | { 48 | _error = new BitmapImage(new Uri("pack://application:,,,/Resources/error_icon.png")); 49 | _error.Freeze(); 50 | } 51 | return _error; 52 | } 53 | private set { } 54 | } 55 | 56 | private static BitmapImage _queued; 57 | public static BitmapImage Queued 58 | { 59 | get 60 | { 61 | if (_queued == null) 62 | { 63 | _queued = new BitmapImage(new Uri("pack://application:,,,/Resources/queued.png")); 64 | _queued.Freeze(); 65 | } 66 | return _queued; 67 | } 68 | private set { } 69 | } 70 | 71 | private static BitmapImage _noAvatar; 72 | public static BitmapImage NoAvatar 73 | { 74 | get 75 | { 76 | if (_noAvatar == null) 77 | { 78 | _noAvatar = new BitmapImage(new Uri("pack://application:,,,/Resources/no_avatar.jpg")); 79 | _noAvatar.Freeze(); 80 | } 81 | return _noAvatar; 82 | } 83 | private set { } 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /NijieDownloader.UI/nijie_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nandaka/NijieDownloader/c7511c6449d8187b365c8ae71afe016e999009b6/NijieDownloader.UI/nijie_icon.ico -------------------------------------------------------------------------------- /NijieDownloader.UI/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /NijieDownloader.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27703.2035 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NijieDownloader.Library", "NijieDownloader.Library\NijieDownloader.Library.csproj", "{6976B954-CD09-4FE6-9880-BF45150A6EF2}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nandaka.Common", "Nandaka.Common\Nandaka.Common.csproj", "{679DC8EF-673E-40CC-8478-08B3CD4BA836}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NijieDownloader.UI", "NijieDownloader.UI\NijieDownloader.UI.csproj", "{91A2504C-9646-4906-BCE9-633253120D8C}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nijie.Test", "Nijie.Test\Nijie.Test.csproj", "{C64EC614-A577-42AB-A67A-21C85A698797}" 13 | EndProject 14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8AF37F8A-492D-4A6B-9F0E-0274E3CB5288}" 15 | ProjectSection(SolutionItems) = preProject 16 | Local.testsettings = Local.testsettings 17 | NijieDownloader.vsmdi = NijieDownloader.vsmdi 18 | TraceAndTestImpact.testsettings = TraceAndTestImpact.testsettings 19 | EndProjectSection 20 | EndProject 21 | Global 22 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 23 | Debug|Any CPU = Debug|Any CPU 24 | Debug|Mixed Platforms = Debug|Mixed Platforms 25 | Debug|x86 = Debug|x86 26 | Release|Any CPU = Release|Any CPU 27 | Release|Mixed Platforms = Release|Mixed Platforms 28 | Release|x86 = Release|x86 29 | EndGlobalSection 30 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 31 | {6976B954-CD09-4FE6-9880-BF45150A6EF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {6976B954-CD09-4FE6-9880-BF45150A6EF2}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {6976B954-CD09-4FE6-9880-BF45150A6EF2}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 34 | {6976B954-CD09-4FE6-9880-BF45150A6EF2}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 35 | {6976B954-CD09-4FE6-9880-BF45150A6EF2}.Debug|x86.ActiveCfg = Debug|Any CPU 36 | {6976B954-CD09-4FE6-9880-BF45150A6EF2}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {6976B954-CD09-4FE6-9880-BF45150A6EF2}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {6976B954-CD09-4FE6-9880-BF45150A6EF2}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 39 | {6976B954-CD09-4FE6-9880-BF45150A6EF2}.Release|Mixed Platforms.Build.0 = Release|Any CPU 40 | {6976B954-CD09-4FE6-9880-BF45150A6EF2}.Release|x86.ActiveCfg = Release|Any CPU 41 | {679DC8EF-673E-40CC-8478-08B3CD4BA836}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 42 | {679DC8EF-673E-40CC-8478-08B3CD4BA836}.Debug|Any CPU.Build.0 = Debug|Any CPU 43 | {679DC8EF-673E-40CC-8478-08B3CD4BA836}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 44 | {679DC8EF-673E-40CC-8478-08B3CD4BA836}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 45 | {679DC8EF-673E-40CC-8478-08B3CD4BA836}.Debug|x86.ActiveCfg = Debug|Any CPU 46 | {679DC8EF-673E-40CC-8478-08B3CD4BA836}.Release|Any CPU.ActiveCfg = Release|Any CPU 47 | {679DC8EF-673E-40CC-8478-08B3CD4BA836}.Release|Any CPU.Build.0 = Release|Any CPU 48 | {679DC8EF-673E-40CC-8478-08B3CD4BA836}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 49 | {679DC8EF-673E-40CC-8478-08B3CD4BA836}.Release|Mixed Platforms.Build.0 = Release|Any CPU 50 | {679DC8EF-673E-40CC-8478-08B3CD4BA836}.Release|x86.ActiveCfg = Release|Any CPU 51 | {91A2504C-9646-4906-BCE9-633253120D8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 52 | {91A2504C-9646-4906-BCE9-633253120D8C}.Debug|Any CPU.Build.0 = Debug|Any CPU 53 | {91A2504C-9646-4906-BCE9-633253120D8C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 54 | {91A2504C-9646-4906-BCE9-633253120D8C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 55 | {91A2504C-9646-4906-BCE9-633253120D8C}.Debug|x86.ActiveCfg = Debug|Any CPU 56 | {91A2504C-9646-4906-BCE9-633253120D8C}.Debug|x86.Build.0 = Debug|Any CPU 57 | {91A2504C-9646-4906-BCE9-633253120D8C}.Release|Any CPU.ActiveCfg = Release|Any CPU 58 | {91A2504C-9646-4906-BCE9-633253120D8C}.Release|Any CPU.Build.0 = Release|Any CPU 59 | {91A2504C-9646-4906-BCE9-633253120D8C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 60 | {91A2504C-9646-4906-BCE9-633253120D8C}.Release|Mixed Platforms.Build.0 = Release|Any CPU 61 | {91A2504C-9646-4906-BCE9-633253120D8C}.Release|x86.ActiveCfg = Release|Any CPU 62 | {91A2504C-9646-4906-BCE9-633253120D8C}.Release|x86.Build.0 = Release|Any CPU 63 | {C64EC614-A577-42AB-A67A-21C85A698797}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 64 | {C64EC614-A577-42AB-A67A-21C85A698797}.Debug|Any CPU.Build.0 = Debug|Any CPU 65 | {C64EC614-A577-42AB-A67A-21C85A698797}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 66 | {C64EC614-A577-42AB-A67A-21C85A698797}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 67 | {C64EC614-A577-42AB-A67A-21C85A698797}.Debug|x86.ActiveCfg = Debug|Any CPU 68 | {C64EC614-A577-42AB-A67A-21C85A698797}.Release|Any CPU.ActiveCfg = Release|Any CPU 69 | {C64EC614-A577-42AB-A67A-21C85A698797}.Release|Any CPU.Build.0 = Release|Any CPU 70 | {C64EC614-A577-42AB-A67A-21C85A698797}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 71 | {C64EC614-A577-42AB-A67A-21C85A698797}.Release|Mixed Platforms.Build.0 = Release|Any CPU 72 | {C64EC614-A577-42AB-A67A-21C85A698797}.Release|x86.ActiveCfg = Release|Any CPU 73 | EndGlobalSection 74 | GlobalSection(SolutionProperties) = preSolution 75 | HideSolutionNode = FALSE 76 | EndGlobalSection 77 | GlobalSection(ExtensibilityGlobals) = postSolution 78 | SolutionGuid = {39C0B0E1-BCFE-4EB7-8441-28B498E275AC} 79 | EndGlobalSection 80 | GlobalSection(TestCaseManagementSettings) = postSolution 81 | CategoryFile = NijieDownloader.vsmdi 82 | EndGlobalSection 83 | EndGlobal 84 | -------------------------------------------------------------------------------- /NijieDownloader.vsmdi: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | NijieDownloader 2 | =============== 3 | 4 | nijie.info downloader 5 | 6 | Feature 7 | =============== 8 | - Image browsing by Member ID. 9 | - Image browsing by Search Tag. 10 | - Batch Image downloading by Image ID, Member ID, or Search Tag. 11 | Donation @ https://bit.ly/NijieDownloader 12 | 13 | Installation 14 | =============== 15 | 1. Install Microsoft SQL Compact 4.0 SP1 (http://www.microsoft.com/en-sg/download/details.aspx?id=30709) 16 | 2. Extract the archive to the desired folder. 17 | 3. Run the exe. 18 | 19 | Required Library for building 20 | =============== 21 | 1. HTML Agility Pack (https://www.nuget.org/packages/HtmlAgilityPack/) 22 | 2. log4net (https://www.nuget.org/packages/log4net/) 23 | 3. EntityFramework.SqlServerCompact 6.0.2 (https://www.nuget.org/packages/EntityFramework.SqlServerCompact/) 24 | 25 | Compiled on Visual Studio 2017 26 | 27 | Supported OS 28 | =============== 29 | WinXP SP2 and up with .Net 4.0 30 | 31 | -------------------------------------------------------------------------------- /TestImage.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nandaka/NijieDownloader/c7511c6449d8187b365c8ae71afe016e999009b6/TestImage.txt -------------------------------------------------------------------------------- /changelog.txt: -------------------------------------------------------------------------------- 1 | Nijie Downloader 1.0.9.6 2 | - Upgrade to .Net45 3 | - Fix Issue #73: need to force to use Tls1.2 (older OS might not supported anymore). 4 | 5 | Nijie Downloader 1.0.9.5 6 | - Fix Issue #63: update parser for workdate and description. 7 | - Fix Issue #64: fix next page detection. 8 | - Fix Issue #65: update login check, you might need to perform age verification in browser. 9 | 10 | Nijie Downloader 1.0.9.4 11 | - Fix Issue #59: update doujin parser. 12 | - Fix Issue #58: update datetime parser. 13 | 14 | Nijie Downloader 1.0.9.2 15 | - Fix Issue #56: Implement video support. 16 | 17 | Nijie Downloader 1.0.9.1 18 | - Fix Issue #51: Update the max page detection for Tag Search. 19 | 20 | Nijie Downloader 1.0.9.0 21 | - Fix Issue #43: Remove duplicated page identifier in filename for manga. 22 | - Implement #44: Remove 'of' from {maxPage}, add {workdate} and {workdate:customformat} for the work date. 23 | - Implement #45: Set File Creation Time and Last-Modified based on server timestamp (configurable). 24 | - Fix Issue #50: Update parser. 25 | 26 | Nijie Downloader 1.0.8.0 27 | - Implement #10: Allow to use list.txt for loading member id for batch jobs. 28 | - Implement #41: Continue with newly added job when the jobs still running. 29 | 30 | Nijie Downloader 1.0.7.9 31 | - Fix Issue#40: update member parser. 32 | 33 | Nijie Downloader 1.0.7.8 34 | - Update member parser. 35 | - Implement #38 and #39: update 404 handler. 36 | - Update libraries. 37 | 38 | Nijie Downloader 1.0.7.7 39 | - Fix Issue #37: Update image parser. 40 | 41 | Nijie Downloader 1.0.7.6 42 | - Fix Issue #36: Update image parser. 43 | - Add option to prevent sleep. 44 | 45 | Nijie Downloader 1.0.7.5 46 | - Bug Fix: Issue#34, Update image parser. 47 | - Bug Fix: Issue#35, Update bookmark parser. 48 | - Enhancement: Issue#22, Add Delete Completed button. 49 | - Update UI. 50 | - Update unit test. 51 | 52 | Nijie Downloader 1.0.7.3 53 | - Bug Fix: Issue#32, update batch job download by tags. 54 | 55 | Nijie Downloader 1.0.7.2 56 | - Feature: Issue#26, Remember filename format. 57 | - Bug Fix: Issue#31, update parser for tag search. 58 | 59 | Nijie Downloader 1.0.7.0 60 | - Feature: Update member parser to parse image count. 61 | - Feature: Update search parser to parse image count. 62 | - Bug Fix: Update web client to handle url ended with period. 63 | - Bug Fix: Update https handler. 64 | - Bug Fix: Update batch job runner. 65 | - Add test case for parsers. 66 | 67 | Nijie Downloader 1.0.6.1 68 | - Feature: Add Member's doujin mode. 69 | - Feature: Add Member's bookmarks mode. 70 | - Enhancement: Add new filenameformat {bookmarkedMemberId} and {bookmarkedMemberName}, only applicable in Member Bookmarks mode. 71 | - Bug Fix: Fix Issue ##21, continue to next image if encounter http error 404/403 for member and tags batch download. 72 | 73 | Nijie Downloader 1.0.5.0 74 | - Feature: Add My Bookmarked Members and My Bookmarked Images (Request #19). 75 | - Feature: Add retry count option. 76 | - Enhancement: Refactor download code in library. 77 | 78 | Nijie Downloader 1.0.4.0 79 | - Feature: Add option to skip image if already downloaded in DB. 80 | - Feature: Add option for the starting page. 81 | - Enhancement: Save manga information to DB. 82 | - Enhancement: Refactor ViewModel and MainWindow. 83 | - Enhancement: Show error list on Batch Job. 84 | - Bug Fix: Update filesize detection. 85 | - Bug Fix: Fix Issue #16, status update in job. 86 | - Bug Fix: Fix Issue #11, Datetime value. 87 | 88 | Nijie Downloader 1.0.3.0 89 | - Feature: Add Trace DB to log setting. 90 | - Feature: Add Job Completion notification dialog. 91 | - Feature: Implement Edit Batch Job item. 92 | - Feature: Add download member avatar for Download by member in setting. 93 | - Feature: Add separate file name format for manga and avatar. 94 | - Feature: Add option to enable network compression. 95 | - Feature: Add option to overwrite only different size (Additional overhead if compression enabled). 96 | - Feature: Add option to backup old file. 97 | - Feature: Add option to enable saving image info to DB. 98 | - Feature: Add option to change log level. 99 | - Enhancement: Add DB lock when inserting. 100 | - Enhancement: Move Add Job as dialog window. 101 | - Bug Fix: Skip locked image in batch mode. 102 | - Bug Fix: Fix date time parser for image. 103 | - Bug Fix: Change header for Batch Download tab. 104 | - Bug Fix: Fix download by member to follow limit. 105 | 106 | Nijie Downloader 1.0.2.0 107 | - Experimental: Add HTTPS support. 108 | - Feature: Add settings to change Concurent Job (require restart). 109 | - Feature: Add settings to add delay between each job. 110 | - Feature: Implement browse/open save directory. 111 | - Feature: Implement delete user.config. 112 | - Bug Fix: Update Start/Stop/Pause logic. 113 | - Bug Fix: Update login logic. 114 | - Bug Fix: Update tag click handler from Image Page. 115 | 116 | Nijie Downloader 1.0.1.0 117 | - Feature: Add DB support. 118 | - Feature: Implement additional search options. 119 | - Feature: Implement additional filename format. 120 | - Feature: Add auto login. 121 | - Feature: Implement Stop/Pause job. 122 | - Feature: Show downloaded image with 50% opacity. 123 | - Feature: Auto save batch list to batchjob.xml 124 | - Bug Fix: Fix checkbox handling on batch list. 125 | - Bug Fix: Fix scrolling in batch list. 126 | - Bug Fix: Always start application on the center of active screen. 127 | - Bug Fix: Fix big image parser. 128 | 129 | Nijie Downloader 1.0.0.2 130 | - Feature: Add save/load batch job. 131 | - Feature: Add custom filename format per job. 132 | - Feature: Add new meta filename format {searchTag} based on the search tags used. 133 | - Feature: Add auto calculate image column count on Member and Search page. 134 | - Feature: Implement Pause/Stop button on batch job. 135 | - Feature: Allow '\' in filename format for path separator. 136 | - Bug Fix: Update error handling. 137 | - Bug Fix: Update big image parser. 138 | 139 | 140 | Nijie Downloader 1.0.0.1 141 | - First release. 142 | - Feature: Browse images by Member ID or Search Tags. 143 | - Feature: Batch Download by Member ID, Image ID, or Search Tags. 144 | - Feature: Custom filename format. 145 | -------------------------------------------------------------------------------- /packages/repositories.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | --------------------------------------------------------------------------------