├── .gitattributes ├── .gitignore ├── .nuget ├── NuGet.Config ├── NuGet.exe └── NuGet.targets ├── Common.cs ├── Exporting ├── CsvExport.cs ├── ExportMethod.cs └── HTMLExport.cs ├── Form1.Designer.cs ├── Form1.cs ├── Form1.resx ├── HttpUtility.cs ├── LICENSE - Json.NET.txt ├── LICENSE.txt ├── Program.cs ├── Properties ├── AssemblyInfo.cs ├── Resources.Designer.cs ├── Resources.resx ├── Settings.Designer.cs └── Settings.settings ├── README.md ├── RedditSaveTransfer.csproj ├── RedditSaveTransfer.sln ├── SavedListing.cs ├── SelectPropertiesWindow.Designer.cs ├── SelectPropertiesWindow.cs ├── SelectPropertiesWindow.resx ├── Threading ├── GetSavedThread.cs ├── LogInThread.cs ├── SavePostThread.cs └── WorkerThread.cs ├── app.config ├── packages.config ├── reddit_save_tool.ico ├── reddit_save_tool.png ├── reddit_save_tool_small.ico └── reddit_save_tool_small.png /.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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | [Dd]ebug/ 46 | [Rr]elease/ 47 | *_i.c 48 | *_p.c 49 | *.ilk 50 | *.meta 51 | *.obj 52 | *.pch 53 | *.pdb 54 | *.pgc 55 | *.pgd 56 | *.rsp 57 | *.sbr 58 | *.tlb 59 | *.tli 60 | *.tlh 61 | *.tmp 62 | *.vspscc 63 | .builds 64 | *.dotCover 65 | 66 | ## TODO: If you have NuGet Package Restore enabled, uncomment this 67 | packages/ 68 | 69 | # Visual C++ cache files 70 | ipch/ 71 | *.aps 72 | *.ncb 73 | *.opensdf 74 | *.sdf 75 | 76 | # Visual Studio profiler 77 | *.psess 78 | *.vsp 79 | 80 | # ReSharper is a .NET coding add-in 81 | _ReSharper* 82 | 83 | # Installshield output folder 84 | [Ee]xpress 85 | 86 | # DocProject is a documentation generator add-in 87 | DocProject/buildhelp/ 88 | DocProject/Help/*.HxT 89 | DocProject/Help/*.HxC 90 | DocProject/Help/*.hhc 91 | DocProject/Help/*.hhk 92 | DocProject/Help/*.hhp 93 | DocProject/Help/Html2 94 | DocProject/Help/html 95 | 96 | # Click-Once directory 97 | publish 98 | 99 | # Others 100 | [Bb]in 101 | [Oo]bj 102 | sql 103 | TestResults 104 | *.Cache 105 | ClientBin 106 | stylecop.* 107 | ~$* 108 | *.dbmdl 109 | Generated_Code #added for RIA/Silverlight projects 110 | 111 | # Backup & report files from converting an old project file to a newer 112 | # Visual Studio version. Backup files are not needed, because we have git ;-) 113 | _UpgradeReport_Files/ 114 | Backup*/ 115 | UpgradeLog*.XML 116 | 117 | 118 | 119 | ############ 120 | ## Windows 121 | ############ 122 | 123 | # Windows image file caches 124 | Thumbs.db 125 | 126 | # Folder config file 127 | Desktop.ini 128 | 129 | 130 | ############# 131 | ## Python 132 | ############# 133 | 134 | *.py[co] 135 | 136 | # Packages 137 | *.egg 138 | *.egg-info 139 | dist 140 | build 141 | eggs 142 | parts 143 | bin 144 | var 145 | sdist 146 | develop-eggs 147 | .installed.cfg 148 | 149 | # Installer logs 150 | pip-log.txt 151 | 152 | # Unit test / coverage reports 153 | .coverage 154 | .tox 155 | 156 | #Translations 157 | *.mo 158 | 159 | #Mr Developer 160 | .mr.developer.cfg 161 | 162 | # Mac crap 163 | .DS_Store 164 | -------------------------------------------------------------------------------- /.nuget/NuGet.Config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.nuget/NuGet.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mavispuford/RedditSaveTransfer/10e80bf0811fb1e39144a80377c3b5ab5b53bd8d/.nuget/NuGet.exe -------------------------------------------------------------------------------- /.nuget/NuGet.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(MSBuildProjectDirectory)\..\ 5 | 6 | 7 | false 8 | 9 | 10 | false 11 | 12 | 13 | true 14 | 15 | 16 | false 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | 30 | $([System.IO.Path]::Combine($(SolutionDir), ".nuget")) 31 | $([System.IO.Path]::Combine($(ProjectDir), "packages.config")) 32 | 33 | 34 | 35 | 36 | $(SolutionDir).nuget 37 | packages.config 38 | 39 | 40 | 41 | 42 | $(NuGetToolsPath)\NuGet.exe 43 | @(PackageSource) 44 | 45 | "$(NuGetExePath)" 46 | mono --runtime=v4.0.30319 $(NuGetExePath) 47 | 48 | $(TargetDir.Trim('\\')) 49 | 50 | -RequireConsent 51 | -NonInteractive 52 | 53 | 54 | $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir "$(SolutionDir) " 55 | $(NuGetCommand) pack "$(ProjectPath)" -Properties Configuration=$(Configuration) $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols 56 | 57 | 58 | 59 | RestorePackages; 60 | $(BuildDependsOn); 61 | 62 | 63 | 64 | 65 | $(BuildDependsOn); 66 | BuildPackage; 67 | 68 | 69 | 70 | 71 | 72 | 73 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | 95 | 97 | 98 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /Common.cs: -------------------------------------------------------------------------------- 1 |  2 | using System.Collections.Generic; 3 | 4 | namespace RedditSaveTransfer 5 | { 6 | public static class Common 7 | { 8 | public const string UserAgent = "Reddit Saved Post Transfer Tool by MavisPuford"; //User Agent string 9 | public const string BaseUrl = "https://www.reddit.com"; 10 | 11 | public static HashSet PropertiesToExport; 12 | public static readonly string[] DefaultPropertiesToExport = { "subreddit", "url", "title", "created_utc" }; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Exporting/CsvExport.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text; 3 | 4 | namespace RedditSaveTransfer.Exporting 5 | { 6 | 7 | /// 8 | /// Exports to a CSV file 9 | /// **Thanks to Chris and Coder Net from stackoverflow.com for example code 10 | /// http://stackoverflow.com/questions/2422212/simple-c-sharp-csv-excel-export-class 11 | /// 12 | public class CsvExport : ExportMethod 13 | { 14 | private readonly List _objects; 15 | 16 | public CsvExport(List objects) 17 | { 18 | _objects = objects; 19 | } 20 | 21 | protected override string Export() 22 | { 23 | var sb = new StringBuilder(); 24 | 25 | // Adds a header line to the csv file 26 | if (_objects.Count > 0) 27 | { 28 | foreach (var pair in _objects[0].Properties) 29 | sb.Append(pair.Key).Append(","); 30 | } 31 | 32 | sb.Remove(sb.Length - 1, 1).AppendLine(); 33 | 34 | // Add value of each property. 35 | foreach (var listing in _objects) 36 | { 37 | foreach (var pair in listing.Properties) 38 | { 39 | sb.Append(CleanUpValue(pair.Value)).Append(","); 40 | } 41 | sb.Remove(sb.Length - 1, 1).AppendLine(); 42 | } 43 | 44 | return sb.ToString(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Exporting/ExportMethod.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data.SqlTypes; 3 | using System.IO; 4 | using System.Text; 5 | 6 | namespace RedditSaveTransfer.Exporting 7 | { 8 | public abstract class ExportMethod 9 | { 10 | protected abstract string Export(); 11 | 12 | /// 13 | /// Exports to bytes 14 | /// 15 | /// 16 | public virtual byte[] ExportToBytes() 17 | { 18 | return Encoding.UTF8.GetBytes(Export() ?? ""); 19 | } 20 | 21 | /// 22 | /// Exports to a file at the given path 23 | /// 24 | /// 25 | public virtual void ExportToFile(string path) 26 | { 27 | File.WriteAllText(path, Export()); 28 | } 29 | 30 | /// 31 | /// Cleans up the given value for export 32 | /// 33 | /// 34 | /// 35 | protected virtual string CleanUpValue(object value) 36 | { 37 | if (value == null) return ""; 38 | if (value is Nullable && ((INullable)value).IsNull) return ""; 39 | 40 | if (value is DateTime) 41 | { 42 | var date = value as DateTime?; 43 | 44 | return date.Value.ToString(date.Value.TimeOfDay.TotalSeconds == 0 ? "yyyy-MM-dd" : "yyyy-MM-dd HH:mm:ss"); 45 | } 46 | 47 | var output = value.ToString(); 48 | 49 | if (output.Contains(",") || output.Contains("\"")) 50 | output = '"' + output.Replace("\"", "\"\"") + '"'; 51 | 52 | if (output.Contains("\n")) 53 | output = output.Replace("\n", ""); 54 | 55 | return output; 56 | } 57 | 58 | /// 59 | /// Shortens the given string according to the limit 60 | /// 61 | /// String to shorten 62 | /// Length limit 63 | /// Shortenes string with "..." appended at the end 64 | protected static string Shorten(string input, int limit) 65 | { 66 | var output = input; 67 | 68 | //Subtracting 3 from the limit just so the ellipsis make it add up to the limit 69 | if (output.Length > limit - 3) 70 | output = output.Substring(0, limit - 3) + "..."; 71 | 72 | return output; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Exporting/HTMLExport.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace RedditSaveTransfer.Exporting 7 | { 8 | /// 9 | /// Exports to an HTML file 10 | /// **Thanks to Chris and Coder Net from stackoverflow.com for example code 11 | /// http://stackoverflow.com/questions/2422212/simple-c-sharp-csv-excel-export-class 12 | /// 13 | public class HtmlExport : ExportMethod 14 | { 15 | private readonly List _objects; 16 | 17 | public HtmlExport(List objects) 18 | { 19 | _objects = objects; 20 | } 21 | 22 | protected override string Export() 23 | { 24 | var sb = new StringBuilder(); 25 | 26 | var fields = Common.PropertiesToExport; 27 | 28 | sb.Append("\n\n"); 29 | 30 | // CSS Styling Stuff 31 | sb.Append("\n"); 32 | sb.Append("\n"); 88 | sb.Append("\n\n"); 89 | 90 | sb.Append("\n\n"); 91 | 92 | if (_objects.Count > 0) 93 | { 94 | sb.Append("\n"); 95 | 96 | sb.Append("\n"); 97 | sb.Append("\n"); 98 | 99 | // Print out the table headers 100 | foreach (var pair in _objects[0].Properties) 101 | { 102 | if (!fields.Contains(pair.Key)) continue; 103 | 104 | switch (pair.Key) 105 | { 106 | case "over_18": 107 | sb.Append("\n"); 108 | break; 109 | case "num_comments": 110 | sb.Append("\n"); 111 | break; 112 | case "created_utc": 113 | sb.Append("\n"); 114 | break; 115 | default: 116 | sb.Append("\n"); 117 | break; 118 | } 119 | } 120 | 121 | sb.Append("\n"); 122 | sb.Append("\n"); 123 | 124 | sb.Append("\n"); 125 | var rowNum = 0; 126 | 127 | // Add the property values to the table 128 | foreach (var item in _objects) 129 | { 130 | sb.Append(rowNum%2 == 0 ? "\n" : "\n"); 131 | 132 | var commentsUrl = Common.BaseUrl + "/comments/"; 133 | 134 | foreach (var pair in item.Properties.Where(pair => pair.Key.Contains("id"))) 135 | { 136 | commentsUrl += pair.Value; 137 | break; 138 | } 139 | 140 | foreach (var pair in item.Properties) 141 | { 142 | if (!fields.Contains(pair.Key)) continue; 143 | 144 | if (pair.Key.Contains("url")) 145 | sb.Append("\n"); 147 | else if (pair.Key.Contains("id")) 148 | sb.Append("\n"); 149 | else if (pair.Key.Contains("over_18")) 150 | sb.Append("\n"); 152 | else if (pair.Key.Contains("created_utc")) 153 | sb.Append("\n"); 156 | else if (pair.Key.Contains("title")) 157 | sb.Append("\n"); 159 | else 160 | sb.Append("\n"); 161 | } 162 | sb.Append("\n\n"); 163 | 164 | sb.Remove(sb.Length - 1, 1).AppendLine(); 165 | rowNum++; 166 | } 167 | 168 | sb.Append("\n"); 169 | sb.Append("
NSFW?commentscreated" + pair.Key).Append("
" + 146 | AddWordBreaks(Shorten(pair.Value, 40), 10) + " " + pair.Value + "" + (bool.Parse(pair.Value) ? "NSFW" : "No") + 151 | "" + 154 | new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds( 155 | double.Parse(pair.Value)).ToShortDateString() + " " + CleanUpValue(pair.Value)) 158 | .Append("" + CleanUpValue(pair.Value)).Append("
\n"); 170 | } 171 | else 172 | { 173 | sb.Append("\n\nNO OBJECTS TO EXPORT
"); 174 | } 175 | 176 | sb.Append("\n\n\n"); 177 | 178 | return sb.ToString(); 179 | } 180 | 181 | /// 182 | /// This is needed for Chrome and possibly other browsers that don't put 183 | /// any word breaks in words that don't have spaces (URLS in this case) 184 | /// 185 | /// Input string 186 | /// How often you want a word break 187 | /// 188 | private static string AddWordBreaks(string input, int spacing) 189 | { 190 | var output = ""; 191 | 192 | for (var i = 0; i < input.Length; i += spacing) 193 | { 194 | if (i == 0) 195 | output += input.Substring(0, spacing) + ""; 196 | else if (i + spacing >= input.Length) 197 | output += input.Substring(i); 198 | else 199 | output += input.Substring(i, spacing) + ""; 200 | } 201 | 202 | return output; 203 | } 204 | } 205 | } -------------------------------------------------------------------------------- /Form1.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace RedditSaveTransfer 2 | { 3 | partial class Form1 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.components = new System.ComponentModel.Container(); 32 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1)); 33 | this.splitContainer1 = new System.Windows.Forms.SplitContainer(); 34 | this.btnClearSelection = new System.Windows.Forms.Button(); 35 | this.btnHelp = new System.Windows.Forms.Button(); 36 | this.label3 = new System.Windows.Forms.Label(); 37 | this.txtPassword1 = new System.Windows.Forms.TextBox(); 38 | this.lblPassword1 = new System.Windows.Forms.Label(); 39 | this.btnLoadSaved = new System.Windows.Forms.Button(); 40 | this.lblUsername1 = new System.Windows.Forms.Label(); 41 | this.txtUsername1 = new System.Windows.Forms.TextBox(); 42 | this.chkMatchRows = new System.Windows.Forms.CheckBox(); 43 | this.chkUnsaveAfter = new System.Windows.Forms.CheckBox(); 44 | this.btnCopyPosts = new System.Windows.Forms.Button(); 45 | this.txtPassword2 = new System.Windows.Forms.TextBox(); 46 | this.label4 = new System.Windows.Forms.Label(); 47 | this.lblPassword2 = new System.Windows.Forms.Label(); 48 | this.txtUsername2 = new System.Windows.Forms.TextBox(); 49 | this.lblUsername2 = new System.Windows.Forms.Label(); 50 | this.splitContainer2 = new System.Windows.Forms.SplitContainer(); 51 | this.statusStrip1 = new System.Windows.Forms.StatusStrip(); 52 | this.toolStripProgressBar1 = new System.Windows.Forms.ToolStripProgressBar(); 53 | this.statusLabel = new System.Windows.Forms.ToolStripStatusLabel(); 54 | this.btnUnsave = new System.Windows.Forms.Button(); 55 | this.btnExport = new System.Windows.Forms.Button(); 56 | this.btnNext = new System.Windows.Forms.Button(); 57 | this.btnPrevious = new System.Windows.Forms.Button(); 58 | this.dataGridView1 = new System.Windows.Forms.DataGridView(); 59 | this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); 60 | this.btnExportOptions = new System.Windows.Forms.Button(); 61 | this.splitContainer1.Panel1.SuspendLayout(); 62 | this.splitContainer1.Panel2.SuspendLayout(); 63 | this.splitContainer1.SuspendLayout(); 64 | this.splitContainer2.Panel1.SuspendLayout(); 65 | this.splitContainer2.Panel2.SuspendLayout(); 66 | this.splitContainer2.SuspendLayout(); 67 | this.statusStrip1.SuspendLayout(); 68 | ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit(); 69 | this.SuspendLayout(); 70 | // 71 | // splitContainer1 72 | // 73 | this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; 74 | this.splitContainer1.Location = new System.Drawing.Point(0, 0); 75 | this.splitContainer1.Name = "splitContainer1"; 76 | // 77 | // splitContainer1.Panel1 78 | // 79 | this.splitContainer1.Panel1.Controls.Add(this.btnClearSelection); 80 | this.splitContainer1.Panel1.Controls.Add(this.btnHelp); 81 | this.splitContainer1.Panel1.Controls.Add(this.label3); 82 | this.splitContainer1.Panel1.Controls.Add(this.txtPassword1); 83 | this.splitContainer1.Panel1.Controls.Add(this.lblPassword1); 84 | this.splitContainer1.Panel1.Controls.Add(this.btnLoadSaved); 85 | this.splitContainer1.Panel1.Controls.Add(this.lblUsername1); 86 | this.splitContainer1.Panel1.Controls.Add(this.txtUsername1); 87 | // 88 | // splitContainer1.Panel2 89 | // 90 | this.splitContainer1.Panel2.Controls.Add(this.chkMatchRows); 91 | this.splitContainer1.Panel2.Controls.Add(this.chkUnsaveAfter); 92 | this.splitContainer1.Panel2.Controls.Add(this.btnCopyPosts); 93 | this.splitContainer1.Panel2.Controls.Add(this.txtPassword2); 94 | this.splitContainer1.Panel2.Controls.Add(this.label4); 95 | this.splitContainer1.Panel2.Controls.Add(this.lblPassword2); 96 | this.splitContainer1.Panel2.Controls.Add(this.txtUsername2); 97 | this.splitContainer1.Panel2.Controls.Add(this.lblUsername2); 98 | this.splitContainer1.Size = new System.Drawing.Size(650, 150); 99 | this.splitContainer1.SplitterDistance = 324; 100 | this.splitContainer1.TabIndex = 0; 101 | this.splitContainer1.TabStop = false; 102 | // 103 | // btnClearSelection 104 | // 105 | this.btnClearSelection.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); 106 | this.btnClearSelection.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 107 | this.btnClearSelection.Location = new System.Drawing.Point(200, 124); 108 | this.btnClearSelection.Name = "btnClearSelection"; 109 | this.btnClearSelection.Size = new System.Drawing.Size(120, 23); 110 | this.btnClearSelection.TabIndex = 10; 111 | this.btnClearSelection.Text = "Clear Selection"; 112 | this.toolTip1.SetToolTip(this.btnClearSelection, "Clear the selection in the table"); 113 | this.btnClearSelection.UseVisualStyleBackColor = true; 114 | this.btnClearSelection.Click += new System.EventHandler(this.btnClearSelection_Click); 115 | // 116 | // btnHelp 117 | // 118 | this.btnHelp.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 119 | this.btnHelp.Location = new System.Drawing.Point(12, 12); 120 | this.btnHelp.Name = "btnHelp"; 121 | this.btnHelp.Size = new System.Drawing.Size(51, 23); 122 | this.btnHelp.TabIndex = 0; 123 | this.btnHelp.Text = "Help"; 124 | this.toolTip1.SetToolTip(this.btnHelp, "WHAT DO I DO?!?!"); 125 | this.btnHelp.UseVisualStyleBackColor = true; 126 | this.btnHelp.Click += new System.EventHandler(this.btnHelp_Click); 127 | // 128 | // label3 129 | // 130 | this.label3.AutoSize = true; 131 | this.label3.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 132 | this.label3.Location = new System.Drawing.Point(109, 13); 133 | this.label3.Name = "label3"; 134 | this.label3.Size = new System.Drawing.Size(99, 17); 135 | this.label3.TabIndex = 4; 136 | this.label3.Text = "Transfer From:"; 137 | this.label3.TextAlign = System.Drawing.ContentAlignment.TopCenter; 138 | // 139 | // txtPassword1 140 | // 141 | this.txtPassword1.Location = new System.Drawing.Point(80, 77); 142 | this.txtPassword1.Name = "txtPassword1"; 143 | this.txtPassword1.Size = new System.Drawing.Size(241, 20); 144 | this.txtPassword1.TabIndex = 2; 145 | this.txtPassword1.UseSystemPasswordChar = true; 146 | // 147 | // lblPassword1 148 | // 149 | this.lblPassword1.AutoSize = true; 150 | this.lblPassword1.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 151 | this.lblPassword1.Location = new System.Drawing.Point(14, 80); 152 | this.lblPassword1.Name = "lblPassword1"; 153 | this.lblPassword1.Size = new System.Drawing.Size(59, 13); 154 | this.lblPassword1.TabIndex = 2; 155 | this.lblPassword1.Text = "Password:"; 156 | // 157 | // btnLoadSaved 158 | // 159 | this.btnLoadSaved.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 160 | this.btnLoadSaved.Location = new System.Drawing.Point(12, 124); 161 | this.btnLoadSaved.Name = "btnLoadSaved"; 162 | this.btnLoadSaved.Size = new System.Drawing.Size(120, 23); 163 | this.btnLoadSaved.TabIndex = 3; 164 | this.btnLoadSaved.Text = "Load Saved Posts"; 165 | this.toolTip1.SetToolTip(this.btnLoadSaved, "Load the saved posts from the LEFT account"); 166 | this.btnLoadSaved.UseVisualStyleBackColor = true; 167 | this.btnLoadSaved.Click += new System.EventHandler(this.btnLoadSaved_Click); 168 | // 169 | // lblUsername1 170 | // 171 | this.lblUsername1.AutoSize = true; 172 | this.lblUsername1.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 173 | this.lblUsername1.Location = new System.Drawing.Point(12, 54); 174 | this.lblUsername1.Name = "lblUsername1"; 175 | this.lblUsername1.Size = new System.Drawing.Size(61, 13); 176 | this.lblUsername1.TabIndex = 1; 177 | this.lblUsername1.Text = "Username:"; 178 | // 179 | // txtUsername1 180 | // 181 | this.txtUsername1.Location = new System.Drawing.Point(80, 51); 182 | this.txtUsername1.Name = "txtUsername1"; 183 | this.txtUsername1.Size = new System.Drawing.Size(241, 20); 184 | this.txtUsername1.TabIndex = 1; 185 | // 186 | // chkMatchRows 187 | // 188 | this.chkMatchRows.AutoSize = true; 189 | this.chkMatchRows.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 190 | this.chkMatchRows.Location = new System.Drawing.Point(8, 126); 191 | this.chkMatchRows.Name = "chkMatchRows"; 192 | this.chkMatchRows.Size = new System.Drawing.Size(131, 17); 193 | this.chkMatchRows.TabIndex = 9; 194 | this.chkMatchRows.Text = "Match selected rows"; 195 | this.toolTip1.SetToolTip(this.chkMatchRows, "Only transfer/unsave/export posts that match the selected rows in the table"); 196 | this.chkMatchRows.UseVisualStyleBackColor = true; 197 | // 198 | // chkUnsaveAfter 199 | // 200 | this.chkUnsaveAfter.AutoSize = true; 201 | this.chkUnsaveAfter.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 202 | this.chkUnsaveAfter.Location = new System.Drawing.Point(8, 103); 203 | this.chkUnsaveAfter.Name = "chkUnsaveAfter"; 204 | this.chkUnsaveAfter.Size = new System.Drawing.Size(217, 17); 205 | this.chkUnsaveAfter.TabIndex = 8; 206 | this.chkUnsaveAfter.Text = "Unsave from left account after saving"; 207 | this.toolTip1.SetToolTip(this.chkUnsaveAfter, "Unsave posts after they have been transferred to the new account"); 208 | this.chkUnsaveAfter.UseVisualStyleBackColor = true; 209 | // 210 | // btnCopyPosts 211 | // 212 | this.btnCopyPosts.Enabled = false; 213 | this.btnCopyPosts.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 214 | this.btnCopyPosts.Location = new System.Drawing.Point(190, 124); 215 | this.btnCopyPosts.Name = "btnCopyPosts"; 216 | this.btnCopyPosts.Size = new System.Drawing.Size(120, 23); 217 | this.btnCopyPosts.TabIndex = 6; 218 | this.btnCopyPosts.Text = "Copy!"; 219 | this.toolTip1.SetToolTip(this.btnCopyPosts, "Copy the posts to the account on the RIGHT"); 220 | this.btnCopyPosts.UseVisualStyleBackColor = true; 221 | this.btnCopyPosts.Click += new System.EventHandler(this.btnCopyPosts_Click); 222 | // 223 | // txtPassword2 224 | // 225 | this.txtPassword2.Location = new System.Drawing.Point(72, 77); 226 | this.txtPassword2.Name = "txtPassword2"; 227 | this.txtPassword2.Size = new System.Drawing.Size(238, 20); 228 | this.txtPassword2.TabIndex = 5; 229 | this.txtPassword2.UseSystemPasswordChar = true; 230 | // 231 | // label4 232 | // 233 | this.label4.AutoSize = true; 234 | this.label4.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 235 | this.label4.Location = new System.Drawing.Point(123, 13); 236 | this.label4.Name = "label4"; 237 | this.label4.Size = new System.Drawing.Size(83, 17); 238 | this.label4.TabIndex = 5; 239 | this.label4.Text = "Transfer To:"; 240 | this.label4.TextAlign = System.Drawing.ContentAlignment.TopCenter; 241 | // 242 | // lblPassword2 243 | // 244 | this.lblPassword2.AutoSize = true; 245 | this.lblPassword2.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 246 | this.lblPassword2.Location = new System.Drawing.Point(7, 80); 247 | this.lblPassword2.Name = "lblPassword2"; 248 | this.lblPassword2.Size = new System.Drawing.Size(59, 13); 249 | this.lblPassword2.TabIndex = 7; 250 | this.lblPassword2.Text = "Password:"; 251 | // 252 | // txtUsername2 253 | // 254 | this.txtUsername2.Location = new System.Drawing.Point(72, 51); 255 | this.txtUsername2.Name = "txtUsername2"; 256 | this.txtUsername2.Size = new System.Drawing.Size(238, 20); 257 | this.txtUsername2.TabIndex = 4; 258 | // 259 | // lblUsername2 260 | // 261 | this.lblUsername2.AutoSize = true; 262 | this.lblUsername2.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 263 | this.lblUsername2.Location = new System.Drawing.Point(5, 54); 264 | this.lblUsername2.Name = "lblUsername2"; 265 | this.lblUsername2.Size = new System.Drawing.Size(61, 13); 266 | this.lblUsername2.TabIndex = 6; 267 | this.lblUsername2.Text = "Username:"; 268 | // 269 | // splitContainer2 270 | // 271 | this.splitContainer2.Dock = System.Windows.Forms.DockStyle.Fill; 272 | this.splitContainer2.FixedPanel = System.Windows.Forms.FixedPanel.Panel1; 273 | this.splitContainer2.IsSplitterFixed = true; 274 | this.splitContainer2.Location = new System.Drawing.Point(0, 0); 275 | this.splitContainer2.Name = "splitContainer2"; 276 | this.splitContainer2.Orientation = System.Windows.Forms.Orientation.Horizontal; 277 | // 278 | // splitContainer2.Panel1 279 | // 280 | this.splitContainer2.Panel1.Controls.Add(this.splitContainer1); 281 | // 282 | // splitContainer2.Panel2 283 | // 284 | this.splitContainer2.Panel2.Controls.Add(this.btnExportOptions); 285 | this.splitContainer2.Panel2.Controls.Add(this.statusStrip1); 286 | this.splitContainer2.Panel2.Controls.Add(this.btnUnsave); 287 | this.splitContainer2.Panel2.Controls.Add(this.btnExport); 288 | this.splitContainer2.Panel2.Controls.Add(this.btnNext); 289 | this.splitContainer2.Panel2.Controls.Add(this.btnPrevious); 290 | this.splitContainer2.Panel2.Controls.Add(this.dataGridView1); 291 | this.splitContainer2.Size = new System.Drawing.Size(650, 494); 292 | this.splitContainer2.SplitterDistance = 150; 293 | this.splitContainer2.TabIndex = 1; 294 | this.splitContainer2.TabStop = false; 295 | // 296 | // statusStrip1 297 | // 298 | this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { 299 | this.toolStripProgressBar1, 300 | this.statusLabel}); 301 | this.statusStrip1.Location = new System.Drawing.Point(0, 318); 302 | this.statusStrip1.Name = "statusStrip1"; 303 | this.statusStrip1.Size = new System.Drawing.Size(650, 22); 304 | this.statusStrip1.TabIndex = 10; 305 | this.statusStrip1.Text = "statusStrip1"; 306 | // 307 | // toolStripProgressBar1 308 | // 309 | this.toolStripProgressBar1.Name = "toolStripProgressBar1"; 310 | this.toolStripProgressBar1.Size = new System.Drawing.Size(100, 16); 311 | // 312 | // statusLabel 313 | // 314 | this.statusLabel.Name = "statusLabel"; 315 | this.statusLabel.Size = new System.Drawing.Size(70, 17); 316 | this.statusLabel.Text = "[List Empty]"; 317 | // 318 | // btnUnsave 319 | // 320 | this.btnUnsave.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); 321 | this.btnUnsave.Enabled = false; 322 | this.btnUnsave.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 323 | this.btnUnsave.Location = new System.Drawing.Point(433, 292); 324 | this.btnUnsave.Name = "btnUnsave"; 325 | this.btnUnsave.Size = new System.Drawing.Size(82, 23); 326 | this.btnUnsave.TabIndex = 5; 327 | this.btnUnsave.Text = "Unsave"; 328 | this.toolTip1.SetToolTip(this.btnUnsave, "Unsave the current post list from the account on the LEFT"); 329 | this.btnUnsave.UseVisualStyleBackColor = true; 330 | this.btnUnsave.Click += new System.EventHandler(this.btnUnsave_Click); 331 | // 332 | // btnExport 333 | // 334 | this.btnExport.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); 335 | this.btnExport.Enabled = false; 336 | this.btnExport.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 337 | this.btnExport.Location = new System.Drawing.Point(521, 292); 338 | this.btnExport.Name = "btnExport"; 339 | this.btnExport.Size = new System.Drawing.Size(87, 23); 340 | this.btnExport.TabIndex = 9; 341 | this.btnExport.Text = "Export"; 342 | this.toolTip1.SetToolTip(this.btnExport, "Export the post list to a CSV or HTML file"); 343 | this.btnExport.UseVisualStyleBackColor = true; 344 | this.btnExport.Click += new System.EventHandler(this.btnExport_Click); 345 | // 346 | // btnNext 347 | // 348 | this.btnNext.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); 349 | this.btnNext.Enabled = false; 350 | this.btnNext.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 351 | this.btnNext.Location = new System.Drawing.Point(101, 292); 352 | this.btnNext.Name = "btnNext"; 353 | this.btnNext.Size = new System.Drawing.Size(83, 23); 354 | this.btnNext.TabIndex = 8; 355 | this.btnNext.Text = "Next"; 356 | this.toolTip1.SetToolTip(this.btnNext, "Go to the next post"); 357 | this.btnNext.UseVisualStyleBackColor = true; 358 | this.btnNext.Click += new System.EventHandler(this.btnNext_Click); 359 | // 360 | // btnPrevious 361 | // 362 | this.btnPrevious.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); 363 | this.btnPrevious.Enabled = false; 364 | this.btnPrevious.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 365 | this.btnPrevious.Location = new System.Drawing.Point(12, 292); 366 | this.btnPrevious.Name = "btnPrevious"; 367 | this.btnPrevious.Size = new System.Drawing.Size(83, 23); 368 | this.btnPrevious.TabIndex = 7; 369 | this.btnPrevious.Text = "Previous"; 370 | this.toolTip1.SetToolTip(this.btnPrevious, "Go to the previous post"); 371 | this.btnPrevious.UseVisualStyleBackColor = true; 372 | this.btnPrevious.Click += new System.EventHandler(this.btnPrevious_Click); 373 | // 374 | // dataGridView1 375 | // 376 | this.dataGridView1.AllowUserToAddRows = false; 377 | this.dataGridView1.AllowUserToDeleteRows = false; 378 | this.dataGridView1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 379 | | System.Windows.Forms.AnchorStyles.Left) 380 | | System.Windows.Forms.AnchorStyles.Right))); 381 | this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; 382 | this.dataGridView1.Location = new System.Drawing.Point(12, 3); 383 | this.dataGridView1.Name = "dataGridView1"; 384 | this.dataGridView1.ReadOnly = true; 385 | this.dataGridView1.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; 386 | this.dataGridView1.Size = new System.Drawing.Size(626, 283); 387 | this.dataGridView1.TabIndex = 1; 388 | // 389 | // btnExportOptions 390 | // 391 | this.btnExportOptions.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); 392 | this.btnExportOptions.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 393 | this.btnExportOptions.Location = new System.Drawing.Point(614, 292); 394 | this.btnExportOptions.Name = "btnExportOptions"; 395 | this.btnExportOptions.Size = new System.Drawing.Size(24, 23); 396 | this.btnExportOptions.TabIndex = 11; 397 | this.btnExportOptions.Text = "..."; 398 | this.toolTip1.SetToolTip(this.btnExportOptions, "Select which properties should be exported to HTML"); 399 | this.btnExportOptions.UseVisualStyleBackColor = true; 400 | this.btnExportOptions.Click += new System.EventHandler(this.btnExportOptions_Click); 401 | // 402 | // Form1 403 | // 404 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 405 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 406 | this.ClientSize = new System.Drawing.Size(650, 494); 407 | this.Controls.Add(this.splitContainer2); 408 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); 409 | this.MinimumSize = new System.Drawing.Size(666, 400); 410 | this.Name = "Form1"; 411 | this.Text = "Reddit Saved Post Transfer Tool - By MavisPuford"; 412 | this.Shown += new System.EventHandler(this.Form1_Shown); 413 | this.splitContainer1.Panel1.ResumeLayout(false); 414 | this.splitContainer1.Panel1.PerformLayout(); 415 | this.splitContainer1.Panel2.ResumeLayout(false); 416 | this.splitContainer1.Panel2.PerformLayout(); 417 | this.splitContainer1.ResumeLayout(false); 418 | this.splitContainer2.Panel1.ResumeLayout(false); 419 | this.splitContainer2.Panel2.ResumeLayout(false); 420 | this.splitContainer2.Panel2.PerformLayout(); 421 | this.splitContainer2.ResumeLayout(false); 422 | this.statusStrip1.ResumeLayout(false); 423 | this.statusStrip1.PerformLayout(); 424 | ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit(); 425 | this.ResumeLayout(false); 426 | 427 | } 428 | 429 | #endregion 430 | 431 | private System.Windows.Forms.SplitContainer splitContainer1; 432 | private System.Windows.Forms.Label lblUsername1; 433 | private System.Windows.Forms.TextBox txtUsername1; 434 | private System.Windows.Forms.Label label3; 435 | private System.Windows.Forms.TextBox txtPassword1; 436 | private System.Windows.Forms.Label lblPassword1; 437 | private System.Windows.Forms.Label label4; 438 | private System.Windows.Forms.SplitContainer splitContainer2; 439 | private System.Windows.Forms.Button btnLoadSaved; 440 | private System.Windows.Forms.DataGridView dataGridView1; 441 | private System.Windows.Forms.Button btnNext; 442 | private System.Windows.Forms.Button btnPrevious; 443 | private System.Windows.Forms.TextBox txtPassword2; 444 | private System.Windows.Forms.Label lblPassword2; 445 | private System.Windows.Forms.TextBox txtUsername2; 446 | private System.Windows.Forms.Label lblUsername2; 447 | private System.Windows.Forms.Button btnCopyPosts; 448 | private System.Windows.Forms.Button btnExport; 449 | private System.Windows.Forms.Button btnHelp; 450 | private System.Windows.Forms.CheckBox chkUnsaveAfter; 451 | private System.Windows.Forms.CheckBox chkMatchRows; 452 | private System.Windows.Forms.Button btnUnsave; 453 | private System.Windows.Forms.Button btnClearSelection; 454 | private System.Windows.Forms.StatusStrip statusStrip1; 455 | private System.Windows.Forms.ToolStripStatusLabel statusLabel; 456 | private System.Windows.Forms.ToolStripProgressBar toolStripProgressBar1; 457 | private System.Windows.Forms.ToolTip toolTip1; 458 | private System.Windows.Forms.Button btnExportOptions; 459 | } 460 | } 461 | 462 | -------------------------------------------------------------------------------- /Form1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Net; 7 | using System.Runtime.Serialization.Formatters.Binary; 8 | using System.Windows.Forms; 9 | using Newtonsoft.Json; 10 | using Newtonsoft.Json.Linq; 11 | using RedditSaveTransfer.Exporting; 12 | using RedditSaveTransfer.Properties; 13 | using RedditSaveTransfer.Threading; 14 | 15 | namespace RedditSaveTransfer 16 | { 17 | public partial class Form1 : Form 18 | { 19 | CookieContainer redditCookie1; //Cookie of the LEFT user account 20 | string cookieFileName1 = "cookie1"; 21 | 22 | CookieContainer redditCookie2; //Cookie of the RIGHT user account 23 | string cookieFileName2 = "cookie2"; 24 | 25 | List cookieJar = new List(); //For keeping track of the different cookie filenames used during the session 26 | 27 | List savedPosts = new List(); //Saved posts that were grabbed from the LEFT account 28 | List toSave = new List(); //Saved posts that will be saved to the RIGHT account 29 | int _currentPost; //Currently selected post (for the DataGridView) 30 | 31 | public Form1() 32 | { 33 | InitializeComponent(); 34 | 35 | redditCookie1 = new CookieContainer(); 36 | redditCookie2 = new CookieContainer(); 37 | 38 | if (Settings.Default.SaveUsername1) 39 | txtUsername1.Text = Settings.Default.Username1; 40 | 41 | if (Settings.Default.SaveUsername2) 42 | txtUsername2.Text = Settings.Default.Username2; 43 | 44 | if (!string.IsNullOrEmpty(Settings.Default.PropertiesToExport)) 45 | { 46 | Common.PropertiesToExport = new HashSet(Settings.Default.PropertiesToExport.Split(',')); 47 | } 48 | else 49 | { 50 | Common.PropertiesToExport = new HashSet(Common.DefaultPropertiesToExport); 51 | Settings.Default.PropertiesToExport = String.Join(",", Common.PropertiesToExport.ToArray()); 52 | Settings.Default.Save(); 53 | } 54 | } 55 | 56 | protected override void OnLoad(EventArgs e) 57 | { 58 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; 59 | //For testing different dpi settings 60 | 61 | //this.Font = new Font(this.Font.FontFamily, this.Font.Size * 120 / 96); 62 | //this.Font = new Font(this.Font.FontFamily, this.Font.Size * 144 / 96); 63 | //this.Font = new Font(this.Font.FontFamily, this.Font.Size * 192 / 96); 64 | base.OnLoad(e); 65 | } 66 | 67 | protected override void OnFormClosed(FormClosedEventArgs e) 68 | { 69 | base.OnFormClosed(e); 70 | 71 | //Delete the cookies that were used 72 | try 73 | { 74 | foreach (var s in cookieJar) 75 | File.Delete(s); 76 | } 77 | catch (Exception ex) 78 | { 79 | Console.WriteLine("Error deleting cookie: " + ex.Message); 80 | } 81 | 82 | } 83 | 84 | private void btnLoadSaved_Click(object sender, EventArgs e) 85 | { 86 | if (!String.IsNullOrEmpty(txtUsername1.Text) && !String.IsNullOrEmpty(txtPassword1.Text)) 87 | { 88 | if (Settings.Default.SaveUsername1) 89 | { 90 | Settings.Default.Username1 = txtUsername1.Text; 91 | Settings.Default.Save(); 92 | } 93 | 94 | btnCopyPosts.Enabled = false; 95 | btnExport.Enabled = false; 96 | btnLoadSaved.Enabled = false; 97 | btnPrevious.Enabled = false; 98 | btnNext.Enabled = false; 99 | btnUnsave.Enabled = false; 100 | 101 | cookieFileName1 = txtUsername1.Text; 102 | redditCookie1 = Loadcookie(cookieFileName1); 103 | 104 | //Add the cookie filename to the cookies list for deletion later 105 | AddToCookieJar(txtUsername1.Text); 106 | 107 | if (redditCookie1 == null) 108 | { 109 | redditCookie1 = new CookieContainer(); 110 | LogIn(true); 111 | } 112 | else 113 | { 114 | GrabPosts(); 115 | } 116 | } 117 | else 118 | { 119 | MessageBox.Show("Make sure the username and password fields are not blank."); 120 | } 121 | } 122 | 123 | private CookieContainer Loadcookie(string filename) 124 | { 125 | CookieContainer container = null; 126 | 127 | try 128 | { 129 | var stream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.None); 130 | var bFormatter = new BinaryFormatter(); 131 | 132 | container = (CookieContainer)bFormatter.Deserialize(stream); 133 | 134 | stream.Close(); 135 | } 136 | catch (Exception e) 137 | { 138 | Console.WriteLine("Problem loading cookie: " + e.Message); 139 | } 140 | 141 | return container; 142 | } 143 | 144 | private void LogIn(bool leftAccount) 145 | { 146 | string username, password, filename; 147 | CookieContainer cookie; 148 | 149 | if (leftAccount) 150 | { 151 | username = txtUsername1.Text; 152 | password = txtPassword1.Text; 153 | cookie = redditCookie1; 154 | filename = cookieFileName1; 155 | } 156 | else 157 | { 158 | username = txtUsername2.Text; 159 | password = txtPassword2.Text; 160 | cookie = redditCookie2; 161 | filename = cookieFileName2; 162 | } 163 | 164 | statusLabel.Text = "Logging In..."; 165 | 166 | //Start the LogInthread 167 | var logInThread = new LogInThread(username, password, ref cookie, filename); 168 | 169 | logInThread.Thread.ProgressChanged += Login_ProgressChanged; 170 | 171 | if (leftAccount) 172 | logInThread.Thread.RunWorkerCompleted += Login_Completed_1; 173 | else 174 | logInThread.Thread.RunWorkerCompleted += Login_Completed_2; 175 | 176 | logInThread.Start(); 177 | } 178 | 179 | /// 180 | /// Adds a cookie filename to the cookies list for deletion later 181 | /// 182 | /// filename to add 183 | void AddToCookieJar(string cookie) 184 | { 185 | if (!cookieJar.Contains(cookie)) 186 | cookieJar.Add(cookie); 187 | } 188 | 189 | void Login_ProgressChanged(object sender, ProgressChangedEventArgs e) 190 | { 191 | toolStripProgressBar1.Value = e.ProgressPercentage; 192 | } 193 | 194 | void Login_Completed_1(object sender, RunWorkerCompletedEventArgs e) 195 | { 196 | if (e.Result != null) 197 | { 198 | var result = (JObject)e.Result; 199 | 200 | //Check for server errors 201 | if (result["json"].SelectToken("errors").HasValues) 202 | { 203 | IList errors = result["json"]["errors"][0].Select(t => (string)t).ToList(); 204 | MessageBox.Show("Error logging in. Server message:\n\"" + errors[1] + "\""); 205 | 206 | statusLabel.Text = "Login Error"; 207 | 208 | btnLoadSaved.Enabled = true; 209 | } 210 | else 211 | { 212 | toolStripProgressBar1.Value = 100; 213 | GrabPosts(); 214 | } 215 | } 216 | else 217 | { 218 | statusLabel.Text = "Login Error"; 219 | 220 | btnLoadSaved.Enabled = true; 221 | } 222 | } 223 | 224 | void Login_Completed_2(object sender, RunWorkerCompletedEventArgs e) 225 | { 226 | if (e.Result != null) 227 | { 228 | var result = (JObject)e.Result; 229 | 230 | //Check for server errors 231 | if (result["json"].SelectToken("errors").HasValues) 232 | { 233 | IList errors = result["json"]["errors"][0].Select(t => (string)t).ToList(); 234 | MessageBox.Show("Error logging in. Server message:\n\"" + errors[1] + "\""); 235 | 236 | statusLabel.Text = "Login Error"; 237 | 238 | btnLoadSaved.Enabled = true; 239 | btnCopyPosts.Enabled = true; 240 | } 241 | else 242 | { 243 | toolStripProgressBar1.Value = 100; 244 | SavePosts(); 245 | } 246 | } 247 | else 248 | { 249 | statusLabel.Text = "Login Error"; 250 | 251 | btnLoadSaved.Enabled = true; 252 | } 253 | } 254 | 255 | void SavePosts() 256 | { 257 | //Filter the list of posts to be saved 258 | FilterPosts(chkMatchRows.Checked); 259 | 260 | //Start the save thread 261 | var davePostThread = new SavePostThread(redditCookie2, toSave, true); 262 | 263 | davePostThread.Thread.ProgressChanged += SavePosts_ProgressChanged; 264 | davePostThread.Thread.RunWorkerCompleted += SavePosts_Completed; 265 | 266 | davePostThread.Start(); 267 | } 268 | 269 | /// 270 | /// Filters the list of saved posts according to which rows were selected in the DataGridView 271 | /// 272 | /// If TRUE, match the selected rows 273 | void FilterPosts(bool matchRows) 274 | { 275 | toSave.Clear(); 276 | 277 | if (!matchRows) 278 | toSave = new List(savedPosts); 279 | else 280 | { 281 | var pulledFromBigList = false; 282 | var toRemove = new List(); 283 | 284 | //Loop through selected rows 285 | foreach (DataGridViewRow c in dataGridView1.SelectedRows) 286 | { 287 | //First run looks through the big list (savedPosts) 288 | if (!pulledFromBigList) 289 | { 290 | pulledFromBigList = true; 291 | foreach (var listing in savedPosts) 292 | { 293 | var add = true; 294 | foreach (var pair in listing.Properties) 295 | { 296 | if (pair.Key != c.Cells[0].Value.ToString()) continue; 297 | 298 | if (pair.Value == c.Cells[1].Value.ToString()) 299 | break; 300 | 301 | add = false; 302 | } 303 | 304 | if (add) 305 | toSave.Add(listing); 306 | } 307 | } 308 | else //Second run and on - look through the filtered list 309 | { 310 | foreach (var listing in toSave) 311 | { 312 | var remove = false; 313 | foreach (var pair in listing.Properties) 314 | { 315 | if (pair.Key != c.Cells[0].Value.ToString()) continue; 316 | 317 | if (pair.Value == c.Cells[1].Value.ToString()) 318 | break; 319 | 320 | remove = true; 321 | } 322 | 323 | if (remove) 324 | toRemove.Add(listing); 325 | } 326 | } 327 | } 328 | 329 | //Remove listings that don't match 330 | foreach (var listing in toRemove) 331 | toSave.Remove(listing); 332 | 333 | Console.WriteLine("Found " + toSave.Count + " posts."); 334 | } 335 | } 336 | 337 | void SavePosts_ProgressChanged(object sender, ProgressChangedEventArgs e) 338 | { 339 | statusLabel.Text = (string)e.UserState; 340 | 341 | toolStripProgressBar1.Value = e.ProgressPercentage; 342 | } 343 | 344 | void SavePosts_Completed(object sender, RunWorkerCompletedEventArgs e) 345 | { 346 | statusLabel.Text = "DONE SAVING"; 347 | 348 | //If we need to unsave the posts in the LEFT account 349 | if (chkUnsaveAfter.Checked) 350 | { 351 | var savePostThread = new SavePostThread(redditCookie1, toSave, false); 352 | 353 | savePostThread.Thread.ProgressChanged += UnSavePosts_ProgressChanged; 354 | savePostThread.Thread.RunWorkerCompleted += UnSavePosts_Completed; 355 | 356 | savePostThread.Start(); 357 | } 358 | else 359 | { 360 | Console.WriteLine("DONE"); 361 | 362 | MessageBox.Show("Finished saving " + toSave.Count + " posts."); 363 | 364 | btnCopyPosts.Enabled = true; 365 | btnLoadSaved.Enabled = true; 366 | btnUnsave.Enabled = true; 367 | 368 | toolStripProgressBar1.Value = 100; 369 | } 370 | } 371 | 372 | void UnSavePosts_ProgressChanged(object sender, ProgressChangedEventArgs e) 373 | { 374 | statusLabel.Text = (string)e.UserState; 375 | toolStripProgressBar1.Value = e.ProgressPercentage; 376 | } 377 | 378 | void UnSavePosts_Completed(object sender, RunWorkerCompletedEventArgs e) 379 | { 380 | Console.WriteLine("DONE"); 381 | 382 | foreach (var listing in toSave) 383 | savedPosts.Remove(listing); 384 | 385 | _currentPost = 0; 386 | dataGridView1.DataSource = null; 387 | 388 | UpdateSelectionText(); 389 | 390 | MessageBox.Show("Finished unsaving " + toSave.Count + " posts."); 391 | 392 | btnCopyPosts.Enabled = true; 393 | btnLoadSaved.Enabled = true; 394 | btnUnsave.Enabled = true; 395 | 396 | toolStripProgressBar1.Value = 100; 397 | } 398 | 399 | void GrabPosts() 400 | { 401 | statusLabel.Text = "Grabbing Saved Posts..."; 402 | 403 | var getSavedThread = new GetSavedThread(redditCookie1); 404 | 405 | getSavedThread.Thread.RunWorkerCompleted += GrabPosts_Completed; 406 | getSavedThread.Thread.ProgressChanged += GrabPosts_ProgressChanged; 407 | 408 | getSavedThread.Start(); 409 | } 410 | 411 | void GrabPosts_ProgressChanged(object sender, ProgressChangedEventArgs e) 412 | { 413 | statusLabel.Text = (string)e.UserState; 414 | 415 | toolStripProgressBar1.Value = e.ProgressPercentage; 416 | } 417 | 418 | void GrabPosts_Completed(object sender, RunWorkerCompletedEventArgs e) 419 | { 420 | savedPosts = (List)e.Result; 421 | 422 | if (savedPosts != null) 423 | { 424 | if (savedPosts.Count > 0) 425 | { 426 | btnCopyPosts.Enabled = true; 427 | btnExport.Enabled = true; 428 | btnPrevious.Enabled = true; 429 | btnNext.Enabled = true; 430 | btnUnsave.Enabled = true; 431 | 432 | Console.WriteLine("Total Posts: " + savedPosts.Count); 433 | 434 | _currentPost = 0; 435 | 436 | dataGridView1.DataSource = null; 437 | dataGridView1.DataSource = savedPosts[_currentPost].Properties; 438 | 439 | dataGridView1.Columns[0].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells; 440 | dataGridView1.Columns[1].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; 441 | 442 | UpdateSelectionText(); 443 | } 444 | else 445 | { 446 | statusLabel.Text = "[No Saved Post Entries]"; 447 | 448 | MessageBox.Show("The saved post list is empty."); 449 | } 450 | } 451 | 452 | btnLoadSaved.Enabled = true; 453 | 454 | toolStripProgressBar1.Value = 100; 455 | } 456 | 457 | //Outputs the given JSON data - for testing 458 | void PrintJson(string json) 459 | { 460 | var reader = new JsonTextReader(new StringReader(json)); 461 | 462 | while (reader.Read()) 463 | { 464 | if (reader.Value != null) 465 | Console.WriteLine("Token: {0}, Value: {1}", reader.TokenType, reader.Value); 466 | else 467 | Console.WriteLine("Token: {0}", reader.TokenType); 468 | } 469 | } 470 | 471 | private void btnNext_Click(object sender, EventArgs e) 472 | { 473 | if (_currentPost + 1 < savedPosts.Count) 474 | _currentPost++; 475 | else 476 | _currentPost = 0; 477 | 478 | if (savedPosts.Count > 0) 479 | { 480 | dataGridView1.DataSource = savedPosts[_currentPost].Properties; 481 | 482 | dataGridView1.Columns[0].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells; 483 | dataGridView1.Columns[1].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; 484 | } 485 | 486 | UpdateSelectionText(); 487 | } 488 | 489 | private void btnPrevious_Click(object sender, EventArgs e) 490 | { 491 | if (_currentPost - 1 >= 0) 492 | _currentPost--; 493 | else 494 | _currentPost = savedPosts.Count - 1; 495 | 496 | if (savedPosts.Count > 0) 497 | { 498 | dataGridView1.DataSource = savedPosts[_currentPost].Properties; 499 | 500 | dataGridView1.Columns[0].AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells; 501 | dataGridView1.Columns[1].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; 502 | } 503 | 504 | UpdateSelectionText(); 505 | } 506 | 507 | private void UpdateSelectionText() 508 | { 509 | if (savedPosts.Count > 0) 510 | statusLabel.Text = "[" + (_currentPost + 1).ToString() + "/" + savedPosts.Count + "]"; 511 | else 512 | statusLabel.Text = "[List Empty]"; 513 | } 514 | 515 | private void btnExport_Click(object sender, EventArgs e) 516 | { 517 | FilterPosts(chkMatchRows.Checked); 518 | 519 | var saveFileDialog = new SaveFileDialog 520 | { 521 | InitialDirectory = Directory.GetCurrentDirectory(), 522 | Filter = 523 | "HTML Files (*.html) |*.html; *.HTML; *.Html |CSV Files (*.csv) |*.csv; *.CSV; *.Csv |All files (*.*)|*.*", 524 | FilterIndex = 0, 525 | RestoreDirectory = true, 526 | Title = "Export " + toSave.Count + " Posts..." 527 | }; 528 | 529 | if (saveFileDialog.ShowDialog() == DialogResult.OK) 530 | { 531 | try 532 | { 533 | Console.WriteLine("File Extension: " + Path.GetExtension(saveFileDialog.FileName.ToLower())); 534 | 535 | switch (Path.GetExtension(saveFileDialog.FileName.ToLower())) 536 | { 537 | case ".csv": 538 | var csv = new CsvExport(toSave); 539 | csv.ExportToFile(saveFileDialog.FileName); 540 | break; 541 | case ".html": 542 | var html = new HtmlExport(toSave); 543 | html.ExportToFile(saveFileDialog.FileName); 544 | break; 545 | } 546 | } 547 | catch (Exception ex) 548 | { 549 | MessageBox.Show("Error: Could not save file to disk. Error: " + ex.Message); 550 | } 551 | } 552 | 553 | } 554 | 555 | private void btnCopyPosts_Click(object sender, EventArgs e) 556 | { 557 | if (!String.IsNullOrEmpty(txtUsername2.Text) && !String.IsNullOrEmpty(txtPassword2.Text)) 558 | { 559 | if (Settings.Default.SaveUsername2) 560 | { 561 | Settings.Default.Username2 = txtUsername2.Text; 562 | Settings.Default.Save(); 563 | } 564 | 565 | var message = ""; 566 | 567 | FilterPosts(chkMatchRows.Checked); 568 | 569 | if (chkUnsaveAfter.Checked && chkMatchRows.Checked) 570 | message = "Are you sure you want to move " + toSave.Count + " post(s) from the left account to the right account?\n\n (This WILL unsave the posts from the LEFT account)"; 571 | else if (chkUnsaveAfter.Checked) 572 | message = "Are you sure you want to move ALL the posts from the left account to the right account?\n\n (This WILL unsave the posts from the LEFT account)"; 573 | else if (chkMatchRows.Checked) 574 | message = "Are you sure you want to COPY " + toSave.Count + " post(s) from the left account to the right account?\n\n (This will NOT unsave the posts from the LEFT account)"; 575 | else 576 | message = "Are you sure you want to COPY all the posts from the left account to the right account?\n\n (This will NOT unsave the posts from the LEFT account)"; 577 | 578 | DialogResult result = MessageBox.Show(message, "Are you sure?", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); 579 | 580 | if (result == DialogResult.Yes) 581 | { 582 | btnCopyPosts.Enabled = false; 583 | btnLoadSaved.Enabled = false; 584 | btnUnsave.Enabled = false; 585 | 586 | cookieFileName2 = txtUsername2.Text; 587 | redditCookie2 = Loadcookie(cookieFileName2); 588 | 589 | //Add the cookie filename to the cookies list for deletion later 590 | AddToCookieJar(txtUsername2.Text); 591 | 592 | if (redditCookie2 == null) 593 | { 594 | redditCookie2 = new CookieContainer(); 595 | LogIn(false); 596 | } 597 | else 598 | { 599 | SavePosts(); 600 | } 601 | } 602 | else if (result == DialogResult.No) 603 | { 604 | btnCopyPosts.Enabled = true; 605 | btnLoadSaved.Enabled = true; 606 | btnUnsave.Enabled = true; 607 | 608 | Console.WriteLine("Unsave Canceled."); 609 | } 610 | } 611 | else 612 | { 613 | MessageBox.Show("Make sure the username and password fields are not blank."); 614 | } 615 | 616 | } 617 | 618 | private void btnUnsave_Click(object sender, EventArgs e) 619 | { 620 | btnCopyPosts.Enabled = false; 621 | btnLoadSaved.Enabled = false; 622 | btnUnsave.Enabled = false; 623 | //btnExport.Enabled = false; 624 | 625 | FilterPosts(chkMatchRows.Checked); 626 | 627 | DialogResult result = MessageBox.Show("This will UNSAVE " + toSave.Count + " posts! Are you sure you want to do this?", "Sure about that?", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); 628 | 629 | if (result == DialogResult.Yes) 630 | { 631 | FilterPosts(chkMatchRows.Checked); 632 | 633 | var savePostThread = new SavePostThread(redditCookie1, toSave, false); 634 | 635 | savePostThread.Thread.ProgressChanged += UnSavePosts_ProgressChanged; 636 | savePostThread.Thread.RunWorkerCompleted += UnSavePosts_Completed; 637 | 638 | savePostThread.Start(); 639 | } 640 | else if (result == DialogResult.No) 641 | { 642 | btnCopyPosts.Enabled = true; 643 | btnLoadSaved.Enabled = true; 644 | btnUnsave.Enabled = true; 645 | 646 | Console.WriteLine("Unsave Canceled."); 647 | } 648 | 649 | } 650 | 651 | private void btnClearSelection_Click(object sender, EventArgs e) 652 | { 653 | dataGridView1.ClearSelection(); 654 | } 655 | 656 | private void btnHelp_Click(object sender, EventArgs e) 657 | { 658 | ShowHelp(); 659 | } 660 | 661 | private void ShowHelp() 662 | { 663 | MessageBox.Show( 664 | "First, put in your credentials and click \"Load Saved Posts\".\n\n" + 665 | "After that, you can export the saved posts to a file (HTML and CSV),\n" + 666 | "unsave them, or copy the posts to another Reddit account\n" + 667 | "by putting in the credentials on the right and clicking \"Copy!\"\n\n" + 668 | "Checking \"Unsave from left account after saving\" unsaves the\n" + 669 | "posts after they have been copied to the new account.\n\n" + 670 | "Checking \"Match selected rows\" transfers/exports only the \n" + 671 | "posts that match the selected rows in the table.\n\n" + 672 | "Want to unsave just one post? Just check \"Match selected rows,\"\n" + 673 | "go to the post and select its id, then click \"Unsave.\"\n\n" + 674 | "**ALSO, IT TAKES A WHILE TO SAVE/UNSAVE POSTS.**\n" + 675 | "They have to be done one at a time, and Reddit's servers have\n" + 676 | "limits on how often you can make requests.\n" + 677 | "(about one request every 2 seconds)" 678 | , "Usage", MessageBoxButtons.OK, MessageBoxIcon.Question); 679 | } 680 | 681 | private void Form1_Shown(object sender, EventArgs e) 682 | { 683 | if (!Settings.Default.FirstRun) return; 684 | 685 | ShowHelp(); 686 | 687 | Settings.Default.FirstRun = false; 688 | Settings.Default.Save(); 689 | } 690 | 691 | private void btnExportOptions_Click(object sender, EventArgs e) 692 | { 693 | var properties = new SelectPropertiesWindow {StartPosition = FormStartPosition.CenterParent}; 694 | 695 | properties.ShowDialog(); 696 | } 697 | 698 | } 699 | } 700 | -------------------------------------------------------------------------------- /HttpUtility.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net; 4 | using System.Text; 5 | 6 | namespace RedditSaveTransfer 7 | { 8 | public static class HttpUtility 9 | { 10 | /// 11 | /// Sends a POST to the specified server 12 | /// 13 | /// URI to send 14 | /// Additional data 15 | /// The cookie to associate with the request 16 | /// Server response 17 | public static string SendPost(string uri, string data, CookieContainer cookie) 18 | { 19 | var request = (HttpWebRequest)WebRequest.Create(uri); 20 | request.CookieContainer = cookie; 21 | request.UserAgent = Common.UserAgent; 22 | request.Method = "POST"; 23 | request.ContentType = "application/x-www-form-urlencoded"; 24 | request.ContentLength = data.Length; 25 | 26 | //Grab the default proxy from IE Internet Settings 27 | var proxy = WebRequest.GetSystemWebProxy(); 28 | var wp = new WebProxy 29 | { 30 | Credentials = proxy.Credentials, 31 | Address = proxy.GetProxy(request.RequestUri) 32 | }; 33 | 34 | // If there is no proxy set up, it will end up being the same as the request Uri 35 | if (!wp.Address.Equals(request.RequestUri)) 36 | { 37 | request.Proxy = wp; 38 | } 39 | 40 | //Encode the data 41 | using (var writeStream = request.GetRequestStream()) 42 | { 43 | var encoding = new UTF8Encoding(); 44 | var bytes = encoding.GetBytes(data); 45 | writeStream.Write(bytes, 0, bytes.Length); 46 | } 47 | 48 | //Send the request and get the server's response 49 | var result = string.Empty; 50 | using (var response = (HttpWebResponse)request.GetResponse()) 51 | { 52 | using (var responseStream = response.GetResponseStream()) 53 | { 54 | using (var readStream = new StreamReader(responseStream, Encoding.UTF8)) 55 | { 56 | result = readStream.ReadToEnd(); 57 | } 58 | } 59 | } 60 | 61 | return result; 62 | } 63 | 64 | /// 65 | /// Sends a GET to the specified server 66 | /// 67 | /// URI to send 68 | /// The cookie to associate with the request 69 | /// Server response 70 | public static string SendGet(string uri, CookieContainer cookie) 71 | { 72 | HttpWebRequest request = null; 73 | 74 | request = (HttpWebRequest)WebRequest.Create(uri); 75 | request.CookieContainer = cookie; 76 | request.UserAgent = Common.UserAgent; 77 | request.Method = "GET"; 78 | request.ContentType = "application/x-www-form-urlencoded"; 79 | request.KeepAlive = false; 80 | request.ProtocolVersion = HttpVersion.Version10; 81 | 82 | //Grab the default proxy from IE Internet Settings 83 | var proxy = WebRequest.GetSystemWebProxy(); 84 | var wp = new WebProxy 85 | { 86 | Credentials = proxy.Credentials, 87 | Address = proxy.GetProxy(request.RequestUri) 88 | }; 89 | 90 | // If there is no proxy set up, it will end up being the same as the request Uri 91 | if (!wp.Address.Equals(request.RequestUri)) 92 | { 93 | request.Proxy = wp; 94 | } 95 | 96 | var result = string.Empty; 97 | 98 | try 99 | { 100 | using (var response = (HttpWebResponse)request.GetResponse()) 101 | { 102 | using (var responseStream = response.GetResponseStream()) 103 | { 104 | using (var readStream = new StreamReader(responseStream, Encoding.UTF8)) 105 | { 106 | result = readStream.ReadToEnd(); 107 | } 108 | } 109 | } 110 | } 111 | catch (Exception e) 112 | { 113 | Console.WriteLine("Error sending GET message: " + e.Message); 114 | } 115 | 116 | return result; 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /LICENSE - Json.NET.txt: -------------------------------------------------------------------------------- 1 |  2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2007 James Newton-King 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 |  2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2012 Blake Ross 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Windows.Forms; 5 | 6 | namespace RedditSaveTransfer 7 | { 8 | static class Program 9 | { 10 | /// 11 | /// The main entry point for the application. 12 | /// 13 | [STAThread] 14 | static void Main() 15 | { 16 | Application.EnableVisualStyles(); 17 | Application.SetCompatibleTextRenderingDefault(false); 18 | try 19 | { 20 | Application.Run(new Form1()); 21 | } 22 | catch (Exception ex) 23 | { 24 | MessageBox.Show("There was a problem! Message: " + ex.InnerException); 25 | } 26 | } 27 | 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /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("RedditSaveTransfer")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("MavisPuford")] 12 | [assembly: AssemblyProduct("RedditSaveTransfer")] 13 | [assembly: AssemblyCopyright("Copyright © MavisPuford")] 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("ed24e57f-d7b6-40c2-90b7-f7c58e92f6e4")] 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.0")] 36 | [assembly: AssemblyFileVersion("1.0.1.0")] 37 | -------------------------------------------------------------------------------- /Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.269 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 RedditSaveTransfer.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RedditSaveTransfer.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Properties/Resources.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 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.34014 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 RedditSaveTransfer.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.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 PropertiesToExport { 30 | get { 31 | return ((string)(this["PropertiesToExport"])); 32 | } 33 | set { 34 | this["PropertiesToExport"] = value; 35 | } 36 | } 37 | 38 | [global::System.Configuration.UserScopedSettingAttribute()] 39 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 40 | [global::System.Configuration.DefaultSettingValueAttribute("")] 41 | public string Username1 { 42 | get { 43 | return ((string)(this["Username1"])); 44 | } 45 | set { 46 | this["Username1"] = value; 47 | } 48 | } 49 | 50 | [global::System.Configuration.UserScopedSettingAttribute()] 51 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 52 | [global::System.Configuration.DefaultSettingValueAttribute("")] 53 | public string Username2 { 54 | get { 55 | return ((string)(this["Username2"])); 56 | } 57 | set { 58 | this["Username2"] = value; 59 | } 60 | } 61 | 62 | [global::System.Configuration.UserScopedSettingAttribute()] 63 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 64 | [global::System.Configuration.DefaultSettingValueAttribute("True")] 65 | public bool SaveUsername1 { 66 | get { 67 | return ((bool)(this["SaveUsername1"])); 68 | } 69 | set { 70 | this["SaveUsername1"] = value; 71 | } 72 | } 73 | 74 | [global::System.Configuration.UserScopedSettingAttribute()] 75 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 76 | [global::System.Configuration.DefaultSettingValueAttribute("True")] 77 | public bool SaveUsername2 { 78 | get { 79 | return ((bool)(this["SaveUsername2"])); 80 | } 81 | set { 82 | this["SaveUsername2"] = value; 83 | } 84 | } 85 | 86 | [global::System.Configuration.UserScopedSettingAttribute()] 87 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 88 | [global::System.Configuration.DefaultSettingValueAttribute("True")] 89 | public bool FirstRun { 90 | get { 91 | return ((bool)(this["FirstRun"])); 92 | } 93 | set { 94 | this["FirstRun"] = value; 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | True 16 | 17 | 18 | True 19 | 20 | 21 | True 22 | 23 | 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Reddit Saved Post Transfer Tool# 2 | By *MavisPuford* 3 | 4 | Yeah, I know. Creative name! :) 5 | 6 | ##DESCRIPTION## 7 | 8 | This tool lets you load up your saved posts from Reddit. From there you can Export them to a HTML/CSV file, save them to another account, or get rid of them. That's about it. 9 | 10 | My main reason for creating this is that I have hundreds of saved posts, and I'm always like: "Hey, I wanna look at all my r/food saved posts in one place." I seem to compulsively save posts and never look at them again because I have to sift through pages of posts on reddit just to get to the one I need (and if I don't know what words were in the title, it's even harder to find). So now I can export my r/food posts to an HTML file, open it up in my browser and look at them all in one place. CTRL + F, "cinnamon". There it is! 11 | 12 | Another reason to use this tool is that reddit only saves 900-ish posts per user account. So if you save posts all the time, you probably lose posts all the time if they aren't being saved somewhere else. 13 | 14 | This is also nice for programs like Evernote, Onenote, whatevernote. Export all your saved posts from r/frugal, import them into whatever program you use, and you've got them all in a place where they are searchable and they won't be lost. 15 | 16 | Or if I want to just have a Reddit account for r/food, I can create one and transfer my r/food saved links to that. 17 | 18 | You get the idea... 19 | 20 | ##INSTRUCTIONS## 21 | 22 | First, put in your credentials and click Load Saved Posts. 23 | 24 | After that, you can export the saved posts to a file (HTML and CSV), unsave them, or copy the posts to another Reddit account by putting in the credentials on the right and clicking Copy! 25 | 26 | Checking Unsave from left account after saving unsaves the posts after they have been copied to the new account. 27 | 28 | Checking Match selected rows transfers/exports only the posts that match the selected rows in the table. 29 | 30 | Want to unsave just one post? Just check Match selected rows, go to the post and select its id, then click Unsave. 31 | 32 | **ALSO, IT TAKES A WHILE TO SAVE/UNSAVE POSTS.** 33 | 34 | They have to be done one at a time, and Reddit's servers have limits on how often you can make requests (about one request every 2 seconds). 35 | 36 | ##SOURCE INFO## 37 | Requires Microsoft .NET Framework 3.5. 38 | 39 | You will also need to enable NuGet package restore which can be found in Visual Studio under "Tools > Options > Package Manager". Then you need to check "Allow NuGet to download missing packages during build". 40 | -------------------------------------------------------------------------------- /RedditSaveTransfer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | x86 6 | 8.0.30703 7 | 2.0 8 | {E36A70C3-D380-4078-A73E-DCD2CA764ECB} 9 | WinExe 10 | Properties 11 | RedditSaveTransfer 12 | RedditSaveTransfer 13 | v3.5 14 | 15 | 16 | 512 17 | false 18 | ..\ 19 | true 20 | publish\ 21 | true 22 | Disk 23 | false 24 | Foreground 25 | 7 26 | Days 27 | false 28 | false 29 | true 30 | 1 31 | 1.0.0.%2a 32 | false 33 | true 34 | true 35 | 36 | 37 | x86 38 | true 39 | full 40 | false 41 | bin\Debug\ 42 | DEBUG;TRACE 43 | prompt 44 | 4 45 | 46 | 47 | AnyCPU 48 | none 49 | true 50 | bin\Release\ 51 | TRACE 52 | prompt 53 | 4 54 | 55 | 56 | reddit_save_tool.ico 57 | 58 | 59 | 008821EF2536F74059CD913289045DD3F99392FA 60 | 61 | 62 | RedditSaveTransfer_TemporaryKey.pfx 63 | 64 | 65 | true 66 | 67 | 68 | false 69 | 70 | 71 | 72 | False 73 | packages\Newtonsoft.Json.6.0.6\lib\net35\Newtonsoft.Json.dll 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | Form 92 | 93 | 94 | Form1.cs 95 | 96 | 97 | 98 | 99 | 100 | 101 | Form 102 | 103 | 104 | SelectPropertiesWindow.cs 105 | 106 | 107 | 108 | 109 | 110 | 111 | Form1.cs 112 | 113 | 114 | ResXFileCodeGenerator 115 | Resources.Designer.cs 116 | Designer 117 | 118 | 119 | True 120 | Resources.resx 121 | True 122 | 123 | 124 | SelectPropertiesWindow.cs 125 | 126 | 127 | 128 | 129 | SettingsSingleFileGenerator 130 | Settings.Designer.cs 131 | 132 | 133 | True 134 | Settings.settings 135 | True 136 | 137 | 138 | 139 | 140 | 141 | 142 | False 143 | Microsoft .NET Framework 4 Client Profile %28x86 and x64%29 144 | true 145 | 146 | 147 | False 148 | .NET Framework 3.5 SP1 Client Profile 149 | false 150 | 151 | 152 | False 153 | .NET Framework 3.5 SP1 154 | false 155 | 156 | 157 | False 158 | Windows Installer 3.1 159 | true 160 | 161 | 162 | 163 | 164 | PreserveNewest 165 | 166 | 167 | PreserveNewest 168 | 169 | 170 | 171 | 172 | 173 | 180 | -------------------------------------------------------------------------------- /RedditSaveTransfer.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RedditSaveTransfer", "RedditSaveTransfer.csproj", "{E36A70C3-D380-4078-A73E-DCD2CA764ECB}" 5 | EndProject 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{C6E3C308-7162-4CA6-BCA7-2A4B63051D17}" 7 | ProjectSection(SolutionItems) = preProject 8 | .nuget\NuGet.Config = .nuget\NuGet.Config 9 | .nuget\NuGet.exe = .nuget\NuGet.exe 10 | .nuget\NuGet.targets = .nuget\NuGet.targets 11 | EndProjectSection 12 | EndProject 13 | Global 14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 15 | Debug|x86 = Debug|x86 16 | Release|x86 = Release|x86 17 | EndGlobalSection 18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 19 | {E36A70C3-D380-4078-A73E-DCD2CA764ECB}.Debug|x86.ActiveCfg = Debug|x86 20 | {E36A70C3-D380-4078-A73E-DCD2CA764ECB}.Debug|x86.Build.0 = Debug|x86 21 | {E36A70C3-D380-4078-A73E-DCD2CA764ECB}.Release|x86.ActiveCfg = Release|x86 22 | {E36A70C3-D380-4078-A73E-DCD2CA764ECB}.Release|x86.Build.0 = Release|x86 23 | EndGlobalSection 24 | GlobalSection(SolutionProperties) = preSolution 25 | HideSolutionNode = FALSE 26 | EndGlobalSection 27 | EndGlobal 28 | -------------------------------------------------------------------------------- /SavedListing.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace RedditSaveTransfer 4 | { 5 | 6 | public class SavedListing 7 | { 8 | public List> Properties = new List>(); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /SelectPropertiesWindow.Designer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Drawing; 4 | using System.Windows.Forms; 5 | 6 | namespace RedditSaveTransfer 7 | { 8 | partial class SelectPropertiesWindow 9 | { 10 | /// 11 | /// Required designer variable. 12 | /// 13 | private IContainer components = null; 14 | 15 | /// 16 | /// Clean up any resources being used. 17 | /// 18 | /// true if managed resources should be disposed; otherwise, false. 19 | protected override void Dispose(bool disposing) 20 | { 21 | if (disposing && (components != null)) 22 | { 23 | components.Dispose(); 24 | } 25 | base.Dispose(disposing); 26 | } 27 | 28 | #region Windows Form Designer generated code 29 | 30 | /// 31 | /// Required method for Designer support - do not modify 32 | /// the contents of this method with the code editor. 33 | /// 34 | private void InitializeComponent() 35 | { 36 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SelectPropertiesWindow)); 37 | this.chkListBoxProps = new System.Windows.Forms.CheckedListBox(); 38 | this.btnAccept = new System.Windows.Forms.Button(); 39 | this.btnCancel = new System.Windows.Forms.Button(); 40 | this.btnDefault = new System.Windows.Forms.Button(); 41 | this.btnSelectAll = new System.Windows.Forms.Button(); 42 | this.btnClear = new System.Windows.Forms.Button(); 43 | this.SuspendLayout(); 44 | // 45 | // chkListBoxProps 46 | // 47 | this.chkListBoxProps.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 48 | | System.Windows.Forms.AnchorStyles.Left) 49 | | System.Windows.Forms.AnchorStyles.Right))); 50 | this.chkListBoxProps.CheckOnClick = true; 51 | this.chkListBoxProps.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 52 | this.chkListBoxProps.FormattingEnabled = true; 53 | this.chkListBoxProps.Items.AddRange(new object[] { 54 | "domain", 55 | "banned_by", 56 | "media_embed", 57 | "subreddit", 58 | "selftext_html", 59 | "selftext", 60 | "likes", 61 | "link_flair_text", 62 | "id", 63 | "clicked", 64 | "title", 65 | "num_comments", 66 | "score", 67 | "approved_by", 68 | "over_18", 69 | "hidden", 70 | "thumbnail", 71 | "subreddit_id", 72 | "edited", 73 | "link_flair_css_class", 74 | "author_flair_css_class", 75 | "downs", 76 | "saved", 77 | "is_self", 78 | "permalink", 79 | "name", 80 | "created", 81 | "url", 82 | "author_flair_text", 83 | "author", 84 | "created_utc", 85 | "media", 86 | "num_reports", 87 | "ups"}); 88 | this.chkListBoxProps.Location = new System.Drawing.Point(12, 12); 89 | this.chkListBoxProps.Name = "chkListBoxProps"; 90 | this.chkListBoxProps.Size = new System.Drawing.Size(266, 208); 91 | this.chkListBoxProps.TabIndex = 0; 92 | // 93 | // btnAccept 94 | // 95 | this.btnAccept.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); 96 | this.btnAccept.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 97 | this.btnAccept.Location = new System.Drawing.Point(12, 255); 98 | this.btnAccept.Name = "btnAccept"; 99 | this.btnAccept.Size = new System.Drawing.Size(130, 23); 100 | this.btnAccept.TabIndex = 1; 101 | this.btnAccept.Text = "Accept"; 102 | this.btnAccept.UseVisualStyleBackColor = true; 103 | this.btnAccept.Click += new System.EventHandler(this.btnAccept_Click); 104 | // 105 | // btnCancel 106 | // 107 | this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); 108 | this.btnCancel.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 109 | this.btnCancel.Location = new System.Drawing.Point(148, 255); 110 | this.btnCancel.Name = "btnCancel"; 111 | this.btnCancel.Size = new System.Drawing.Size(130, 23); 112 | this.btnCancel.TabIndex = 2; 113 | this.btnCancel.Text = "Cancel"; 114 | this.btnCancel.UseVisualStyleBackColor = true; 115 | // 116 | // btnDefault 117 | // 118 | this.btnDefault.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); 119 | this.btnDefault.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 120 | this.btnDefault.Location = new System.Drawing.Point(100, 226); 121 | this.btnDefault.Name = "btnDefault"; 122 | this.btnDefault.Size = new System.Drawing.Size(91, 23); 123 | this.btnDefault.TabIndex = 3; 124 | this.btnDefault.Text = "Default"; 125 | this.btnDefault.UseVisualStyleBackColor = true; 126 | this.btnDefault.Click += new System.EventHandler(this.btnDefault_Click); 127 | // 128 | // btnSelectAll 129 | // 130 | this.btnSelectAll.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); 131 | this.btnSelectAll.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 132 | this.btnSelectAll.Location = new System.Drawing.Point(12, 226); 133 | this.btnSelectAll.Name = "btnSelectAll"; 134 | this.btnSelectAll.Size = new System.Drawing.Size(82, 23); 135 | this.btnSelectAll.TabIndex = 4; 136 | this.btnSelectAll.Text = "Select All"; 137 | this.btnSelectAll.UseVisualStyleBackColor = true; 138 | this.btnSelectAll.Click += new System.EventHandler(this.btnSelectAll_Click); 139 | // 140 | // btnClear 141 | // 142 | this.btnClear.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); 143 | this.btnClear.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 144 | this.btnClear.Location = new System.Drawing.Point(197, 226); 145 | this.btnClear.Name = "btnClear"; 146 | this.btnClear.Size = new System.Drawing.Size(81, 23); 147 | this.btnClear.TabIndex = 5; 148 | this.btnClear.Text = "Clear"; 149 | this.btnClear.UseVisualStyleBackColor = true; 150 | this.btnClear.Click += new System.EventHandler(this.btnClear_Click); 151 | // 152 | // SelectPropertiesWindow 153 | // 154 | this.ClientSize = new System.Drawing.Size(290, 290); 155 | this.Controls.Add(this.btnClear); 156 | this.Controls.Add(this.btnSelectAll); 157 | this.Controls.Add(this.btnDefault); 158 | this.Controls.Add(this.btnCancel); 159 | this.Controls.Add(this.btnAccept); 160 | this.Controls.Add(this.chkListBoxProps); 161 | this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 162 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); 163 | this.MinimumSize = new System.Drawing.Size(306, 328); 164 | this.Name = "SelectPropertiesWindow"; 165 | this.Text = "HTML Export Properties..."; 166 | this.ResumeLayout(false); 167 | 168 | } 169 | 170 | #endregion 171 | 172 | private Button btnAccept; 173 | private Button btnCancel; 174 | public CheckedListBox chkListBoxProps; 175 | private Button btnDefault; 176 | private Button btnSelectAll; 177 | private Button btnClear; 178 | } 179 | } -------------------------------------------------------------------------------- /SelectPropertiesWindow.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Windows.Forms; 4 | using RedditSaveTransfer.Properties; 5 | 6 | namespace RedditSaveTransfer 7 | { 8 | public partial class SelectPropertiesWindow : Form 9 | { 10 | 11 | public SelectPropertiesWindow() 12 | { 13 | InitializeComponent(); 14 | 15 | AcceptButton = btnAccept; 16 | CancelButton = btnCancel; 17 | 18 | SetCheckedItems(); 19 | } 20 | 21 | private void SetPropertiesToExport() 22 | { 23 | Common.PropertiesToExport.Clear(); 24 | 25 | foreach (var p in chkListBoxProps.CheckedItems) 26 | Common.PropertiesToExport.Add(p.ToString()); 27 | } 28 | 29 | private void SetCheckedItems() 30 | { 31 | foreach (var p in Common.PropertiesToExport) 32 | { 33 | var index = chkListBoxProps.Items.IndexOf(p); 34 | 35 | if (index < 0 || index >= chkListBoxProps.Items.Count) 36 | continue; 37 | 38 | chkListBoxProps.SetItemChecked(index, true); 39 | 40 | Console.WriteLine(p); 41 | } 42 | } 43 | 44 | private void btnAccept_Click(object sender, EventArgs e) 45 | { 46 | if (chkListBoxProps.CheckedItems.Count == 0) 47 | { 48 | MessageBox.Show("You must have at least one property selected."); 49 | return; 50 | } 51 | 52 | SetPropertiesToExport(); 53 | 54 | if (Common.PropertiesToExport != null && Common.PropertiesToExport.Any()) 55 | { 56 | Settings.Default.PropertiesToExport = String.Join(",", Common.PropertiesToExport.ToArray()); 57 | Settings.Default.Save(); 58 | } 59 | 60 | DialogResult = DialogResult.OK; 61 | } 62 | 63 | private void btnDefault_Click(object sender, EventArgs e) 64 | { 65 | CheckUncheckAll(false); 66 | 67 | foreach (var p in Common.DefaultPropertiesToExport) 68 | chkListBoxProps.SetItemChecked(chkListBoxProps.Items.IndexOf(p), true); 69 | } 70 | 71 | private void btnSelectAll_Click(object sender, EventArgs e) 72 | { 73 | CheckUncheckAll(true); 74 | } 75 | 76 | private void btnClear_Click(object sender, EventArgs e) 77 | { 78 | CheckUncheckAll(false); 79 | } 80 | 81 | private void CheckUncheckAll(bool itemChecked) 82 | { 83 | for (var i = 0; i < chkListBoxProps.Items.Count; i++) 84 | chkListBoxProps.SetItemChecked(i, itemChecked); 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /Threading/GetSavedThread.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | using System.Windows.Forms; 5 | using Newtonsoft.Json.Linq; 6 | 7 | namespace RedditSaveTransfer.Threading 8 | { 9 | /// 10 | /// Grabs the saved posts from the specified Reddit account 11 | /// 12 | class GetSavedThread : WorkerThread 13 | { 14 | private readonly CookieContainer _cookie; //Cookie that will be used 15 | 16 | public GetSavedThread(CookieContainer cookie) 17 | { 18 | _cookie = cookie; 19 | } 20 | 21 | protected override void thread_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) 22 | { 23 | base.thread_DoWork(sender, e); 24 | 25 | var savedPosts = GrabSaved(); 26 | 27 | savedPosts.Reverse(); 28 | 29 | e.Result = savedPosts; 30 | } 31 | 32 | /// 33 | /// Grabs the saved posts 34 | /// 35 | /// List of posts 36 | private List GrabSaved() 37 | { 38 | var currentId = "current"; 39 | var posts = new List(); 40 | var savedChunks = new List(); 41 | var emptyResultCount = 0; 42 | 43 | // Loop until we dont have an "after" id 44 | do 45 | { 46 | Thread.ReportProgress(savedChunks.Count * 10, "Working... (Saved Chunks: " + savedChunks.Count + ")"); 47 | 48 | Console.WriteLine("Saved Chunks: " + savedChunks.Count); 49 | 50 | var result = ""; 51 | if (currentId != "current") 52 | result = HttpUtility.SendGet(Common.BaseUrl + "/saved.json?limit=100&after=" + currentId, _cookie); 53 | else 54 | result = HttpUtility.SendGet(Common.BaseUrl + "/saved.json?limit=100", _cookie); 55 | 56 | if (string.IsNullOrEmpty(result)) 57 | { 58 | emptyResultCount++; 59 | 60 | if (emptyResultCount >= 3) 61 | { 62 | MessageBox.Show("The request returned 0 results. Please try again."); 63 | return posts; 64 | } 65 | } 66 | else 67 | { 68 | savedChunks.Add(JObject.Parse(result)); 69 | 70 | currentId = (string)savedChunks[savedChunks.Count - 1]["data"].SelectToken("after"); 71 | 72 | Console.WriteLine("Current ID: " + currentId); 73 | } 74 | 75 | System.Threading.Thread.Sleep(2000); 76 | 77 | } while (currentId != null); 78 | 79 | Console.WriteLine("Final Chunk Count: " + savedChunks.Count); 80 | 81 | // Add posts to the list 82 | foreach (var j in savedChunks) 83 | { 84 | foreach (var child in j["data"]["children"]) 85 | { 86 | var listing = new SavedListing(); 87 | 88 | foreach (var jToken in child["data"]) 89 | { 90 | var jProp = (JProperty) jToken; 91 | listing.Properties.Add(new KeyValuePair(jProp.Name, jProp.Value.ToString())); 92 | } 93 | 94 | posts.Add(listing); 95 | } 96 | } 97 | 98 | return posts; 99 | } 100 | 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /Threading/LogInThread.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Net; 5 | using System.Runtime.Serialization.Formatters.Binary; 6 | using Newtonsoft.Json.Linq; 7 | 8 | namespace RedditSaveTransfer.Threading 9 | { 10 | class LogInThread : WorkerThread 11 | { 12 | private readonly string _username; //Username of the account 13 | private readonly string _password; //Password of the account 14 | 15 | private readonly CookieContainer _cookie; //Cookie that will be used 16 | private readonly string _cookieFileName; //Filename of the cookie 17 | 18 | public LogInThread(string username, string password, ref CookieContainer cookie, string cookieFileName) 19 | { 20 | _username = username; 21 | _password = password; 22 | _cookie = cookie; 23 | _cookieFileName = cookieFileName; 24 | } 25 | 26 | protected override void thread_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) 27 | { 28 | base.thread_DoWork(sender, e); 29 | 30 | Thread.ReportProgress(0); 31 | 32 | var result = LogIn(); 33 | 34 | Console.WriteLine("LogIn result: " + result); 35 | 36 | e.Result = result; 37 | } 38 | 39 | /// 40 | /// Logs into the reddit servers 41 | /// 42 | /// Server response 43 | private JObject LogIn() 44 | { 45 | var url = Common.BaseUrl + "/api/login/" + _username; 46 | var body = new Dictionary 47 | { 48 | {"user", _username}, 49 | {"passwd", _password} 50 | }; 51 | 52 | var postData = string.Format("api_type=json&user={0}&passwd={1}", body["user"], body["passwd"]); 53 | 54 | var result = HttpUtility.SendPost(url, postData, _cookie); 55 | 56 | var jResult = JObject.Parse(result); 57 | 58 | //If there are no errors 59 | if (!jResult["json"].SelectToken("errors").HasValues) 60 | { 61 | //Save the cookie 62 | SaveCookie(_cookie, _cookieFileName); 63 | } 64 | 65 | return jResult; 66 | } 67 | 68 | /// 69 | /// Saves the cookie to a temporary file 70 | /// 71 | /// CookieContainer 72 | /// Filename of the cookie 73 | private static void SaveCookie(CookieContainer container, string filename) 74 | { 75 | try 76 | { 77 | Console.WriteLine("Writing cookie \"" + filename + "\""); 78 | Stream stream = File.Open(filename, FileMode.Create); 79 | var bFormatter = new BinaryFormatter(); 80 | bFormatter.Serialize(stream, container); 81 | stream.Close(); 82 | } 83 | catch (Exception e) 84 | { 85 | Console.WriteLine("Problem writing cookie \"" + filename + "\"" + ": " + e.Message); 86 | } 87 | } 88 | 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Threading/SavePostThread.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | using Newtonsoft.Json.Linq; 5 | 6 | namespace RedditSaveTransfer.Threading 7 | { 8 | /// 9 | /// Saves/Unsaves the given list of posts to the specified Reddit account 10 | /// 11 | class SavePostThread : WorkerThread 12 | { 13 | readonly List _postsToSave = new List(); 14 | 15 | private readonly CookieContainer _cookie; //Cookie that will be used 16 | private string _modHash; //Modhash of the user 17 | private readonly bool _save; //TRUE = SAVE, FALSE = UNSAVE 18 | 19 | public SavePostThread(CookieContainer cookie, List posts, bool save) 20 | { 21 | _cookie = cookie; 22 | _postsToSave = posts; 23 | _save = save; 24 | } 25 | 26 | protected override void thread_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) 27 | { 28 | base.thread_DoWork(sender, e); 29 | 30 | Thread.ReportProgress(0); 31 | 32 | if (GetModHash()) 33 | SavePosts(); 34 | 35 | Thread.ReportProgress(100); 36 | } 37 | 38 | /// 39 | /// Gets the user's modhash so we can use it to send a POST 40 | /// 41 | /// True if successful (the modhash is not null or empty) 42 | private bool GetModHash() 43 | { 44 | var response = HttpUtility.SendGet(Common.BaseUrl + "/api/me.json", _cookie); 45 | 46 | var jObject = JObject.Parse(response); 47 | 48 | var property = (JValue) jObject["data"].SelectToken("modhash", false); 49 | 50 | if (property != null) 51 | { 52 | _modHash = property.Value.ToString(); 53 | return true; 54 | } 55 | 56 | return false; 57 | } 58 | 59 | /// 60 | /// Saves the posts to the Reddit account 61 | /// 62 | private void SavePosts() 63 | { 64 | var count = 1; 65 | 66 | foreach (var listing in _postsToSave) 67 | { 68 | var progress = (int)((( (double)count - 1 ) / _postsToSave.Count) * 100); 69 | 70 | foreach (var pair in listing.Properties) 71 | { 72 | if (pair.Key != "name") continue; 73 | 74 | //If we are SAVING 75 | if (_save) 76 | { 77 | Console.WriteLine("Saving \"" + pair.Value + "\""); 78 | Thread.ReportProgress(progress, "Saving Post " + count + " of " + _postsToSave.Count); 79 | 80 | Console.WriteLine("Progress: " + (int)((( (double)count - 1 ) / _postsToSave.Count) * 100)); 81 | 82 | //SEND POST 83 | Console.WriteLine("POST: " + Common.BaseUrl + "/api/save?id=" + pair.Value + "&uh=" + _modHash); 84 | Console.WriteLine("RESPONSE: " + HttpUtility.SendPost(Common.BaseUrl + "/api/save", "id=" + pair.Value + "&uh=" + _modHash, _cookie)); 85 | } 86 | else //If we are UNSAVING 87 | { 88 | Console.WriteLine("Unsaving \"" + pair.Value + "\""); 89 | Thread.ReportProgress(progress, "Unsaving Post " + count + " of " + _postsToSave.Count); 90 | 91 | //SEND POST 92 | Console.WriteLine("POST: " + Common.BaseUrl + "/api/unsave?id=" + pair.Value + "&uh=" + _modHash); 93 | Console.WriteLine("RESPONSE: " + HttpUtility.SendPost(Common.BaseUrl + "/api/unsave", "id=" + pair.Value + "&uh=" + _modHash, _cookie)); 94 | } 95 | 96 | //SLEEP 2000 MS 97 | System.Threading.Thread.Sleep(2000); 98 | 99 | count++; 100 | break; 101 | } 102 | } 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /Threading/WorkerThread.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | namespace RedditSaveTransfer.Threading 4 | { 5 | /// 6 | /// Basic class used for threading 7 | /// 8 | public class WorkerThread 9 | { 10 | public BackgroundWorker Thread { get; private set; } 11 | 12 | public WorkerThread() 13 | { 14 | Thread = new BackgroundWorker { WorkerReportsProgress = true, WorkerSupportsCancellation = true }; 15 | 16 | Thread.DoWork += thread_DoWork; 17 | Thread.ProgressChanged += thread_ProgressChanged; 18 | Thread.RunWorkerCompleted += thread_RunWorkerCompleted; 19 | } 20 | 21 | public void Start() 22 | { 23 | Thread.RunWorkerAsync(); 24 | } 25 | 26 | public void Stop() 27 | { 28 | Thread.CancelAsync(); 29 | Thread.Dispose(); 30 | } 31 | 32 | protected virtual void thread_ProgressChanged(object sender, ProgressChangedEventArgs e) 33 | { 34 | 35 | } 36 | 37 | protected virtual void thread_DoWork(object sender, DoWorkEventArgs e) 38 | { 39 | if (Thread.CancellationPending) 40 | return; 41 | } 42 | 43 | protected virtual void thread_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 44 | { 45 | 46 | } 47 | 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | True 21 | 22 | 23 | True 24 | 25 | 26 | True 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /reddit_save_tool.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mavispuford/RedditSaveTransfer/10e80bf0811fb1e39144a80377c3b5ab5b53bd8d/reddit_save_tool.ico -------------------------------------------------------------------------------- /reddit_save_tool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mavispuford/RedditSaveTransfer/10e80bf0811fb1e39144a80377c3b5ab5b53bd8d/reddit_save_tool.png -------------------------------------------------------------------------------- /reddit_save_tool_small.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mavispuford/RedditSaveTransfer/10e80bf0811fb1e39144a80377c3b5ab5b53bd8d/reddit_save_tool_small.ico -------------------------------------------------------------------------------- /reddit_save_tool_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mavispuford/RedditSaveTransfer/10e80bf0811fb1e39144a80377c3b5ab5b53bd8d/reddit_save_tool_small.png --------------------------------------------------------------------------------