Skip to content
This repository has been archived by the owner on Jul 1, 2024. It is now read-only.

Commit

Permalink
Merge pull request #14 from LanceMcCarthy/dev
Browse files Browse the repository at this point in the history
Improved console app, added lots of UWP features and improvements.
  • Loading branch information
LanceMcCarthy authored Jul 30, 2019
2 parents 50afae5 + 4bf6510 commit cb88963
Show file tree
Hide file tree
Showing 6 changed files with 423 additions and 223 deletions.
32 changes: 20 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,51 @@
An application that lets you quickly download 3D models in bulk from Remix 3D. This functionality is not available via the website.


### First Step - Get the User ID
### First Step - Getting the User ID

To browse models, you'll need a Remix3D.com user ID code. You can easily get that code from the Remix3D website in the web browser address bar. Here are the steps to load models.

1. Go to a user's page in [Remix3D.com](https://www.remix3d.com)
2. Look in the address bar and get the UserID code, it is between `user/` and the question mark (see screenshot below).
3. In the app, paste that code into the `User ID` box and click `Get User Models` button.
4. It will automatically load the models for that user as you scroll.
3. Use that code in the app when you're asked for a User ID.

![image](https://user-images.githubusercontent.com/3520532/61412729-9f80a300-a8b7-11e9-912f-c899c6889b0e.png)

> There is a default User ID `46rbnCYv5fy` in the app to help you get started, it's the **Xbox** account. Another example is `46reU3-wFPz`, this is the **Microsoft** account seen in the screenshot above (2,139 models).
### Console App - Automatic bulk downloader

Run the console app,
Run the console app, and take the following steps.

1. Enter the user's ID
2. Enter the folder path
3. Enter Y/N if you want the HoloLens and Mixed Reality files, too.
1. Enter the **User ID** code.
2. Enter the **folder path** you want to save the files in (e.g. `C:\Users\Lance\Downloads\`). *The app will automatically organize subfolders for you*.
3. Enter **Y** or **N** if you want the HoloLens and Mixed Reality files, too.

The app will download the entire model library for that user. This will take time, approximately 15-20 second for every 10 models. If that user hs 2,000 models, do the math :)
The app will download the entire model library for that user. This may take some time, see the GIF below for the average download time for each model. If that user has 2,000 models, do the math :)

![image](https://dvlup.blob.core.windows.net/general-app-files/GIFs/RemixDownloaderConsoleGif.gif)
![image](https://dvlup.blob.core.windows.net/general-app-files/GIFs/Remix3DDownloaderConsoleBetter.gif)

### UWP App - Selection and Download


Here are the steps to select and download model files.

1. Select models in the list, multiple selection is enabled (CTRL + Click, Shift + Click, etc). You will see your selections populate the list on the right.
Enter the User ID in the UserID box, then click either **Load User Models** or **Download All Models** button.

*If you chose List User Models:*

1. Select models in the GridView, multiple selection is enabled (CTRL + Click). You will see your selections populate the ListView on the right.
2. When you're ready to download, choose a `Level of Detail` and a `Download folder` location
3. Click the `Start Download` button, the app will download all of the selected models to the selected folder.

![image](https://user-images.githubusercontent.com/3520532/61333383-bdd19a80-a7f4-11e9-89e9-5b0b7faf01c1.png)
*If you choose Download All Models:*

This is a long running process and <u>could take more than an hour</u> depending on how many models that user has. For example, the Microsoft and XBox users have more than 2,000 models! Please be patient, the process will stop once there are no more items for that user.

![image](https://user-images.githubusercontent.com/3520532/62094015-92fe3200-b249-11e9-8cc8-e5982308906b.png)


> This is an early alpha. Only `User ID` is working. Support for Board collections support will be added soon.
> This is an early beta and I'm the only developer. Please report any issues in <a href="https://github.com/LanceMcCarthy/RemixDownloader/issues" target="_blank">the Issues tab</a> and I will get to them as soon as possible.
### Printing Model Files

Expand Down
145 changes: 89 additions & 56 deletions src/RemixDownloader/RemixDownloader.Console/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
using System.Threading.Tasks;
using RemixDownloader.Core.Models;
using RemixDownloader.Core.Services;
using static System.Console;

namespace RemixDownloader.Console
{
Expand All @@ -22,37 +21,44 @@ class Program

public static async Task Main(string[] args)
{
WriteLine("Enter the Remix3D User ID (e.g. 46rbnCYv5fy). Press Enter for XBox UserId: ");
System.Console.Title = "Remix3D Downloader";

var userId = ReadLine();
UpdateStatus("Enter the Remix3D User ID (default:'46rbnCYv5fy' aka XBox):", ConsoleColor.White);

var userId = System.Console.ReadLine();

if (string.IsNullOrEmpty(userId))
{
userId = "46rbnCYv5fy";
}

WriteLine("Enter folder path to save files to (e.g. c:\\Users\\You\\Downloads\\). Press Enter for app directory:");
UpdateStatus("Enter folder path to save files to (e.g. c:\\Users\\You\\Downloads\\). (default: \\appfolder\\Downloads\\):", ConsoleColor.White);

var folderPath = ReadLine();
var folderPath = System.Console.ReadLine();

if (string.IsNullOrEmpty(userId))
{
folderPath = Directory.GetCurrentDirectory();
folderPath = Path.Combine(Directory.GetCurrentDirectory(),"Downloads");
}

WriteLine("Do you want to also download optimized HoloLens and WinMR models (Y/N)? This will add to the download time, but saves you a lot of conversion work later! Press Enter for N:");
//WriteLine("Do you want to also download optimized HoloLens and WinMR models (Y/N)? This will add to the download time, but saves you a lot of conversion work later! Press Enter for N:");
UpdateStatus("Do you want to also download optimized HoloLens and WinMR models? (default: N)?\r" +
"\n- [Y/y] Yes, save the pre-optimized versions models, too (this will save conversion time later)." +
"\n- [N/n] No, only save the original model file.",
ConsoleColor.White);

var includeOptimizedString = ReadLine()?.ToLower();
var includeOptimizedString = System.Console.ReadLine()?.ToLower();

if (string.IsNullOrEmpty(includeOptimizedString))
{
includeOptimizedString = "n";
}

bool includeOptimized = includeOptimizedString == "y";
bool includeOptimized = includeOptimizedString == "y" ||
includeOptimizedString == "e" ||
includeOptimizedString == "s";

ForegroundColor = ConsoleColor.Yellow;
WriteLine("Download starting, use CTRL+C to cancel at any time.");
UpdateStatus("Download starting! This will likely take a long time, use CTRL+C to cancel at any time.", ConsoleColor.Green);

var userDirectoryPath = Path.Combine(folderPath, userId);
var userDirectory = Directory.CreateDirectory(userDirectoryPath);
Expand All @@ -69,7 +75,7 @@ public static async Task Main(string[] args)
_cancellationTokenSource = new CancellationTokenSource();
_cancellationToken = _cancellationTokenSource.Token;

CancelKeyPress += Console_CancelKeyPress;
System.Console.CancelKeyPress += Console_CancelKeyPress;

string contUrl = string.Empty;

Expand Down Expand Up @@ -102,13 +108,13 @@ public static async Task Main(string[] args)

downloadCount += 10;

ForegroundColor = ConsoleColor.Green;
WriteLine($"{downloadCount} models downloaded...");
UpdateStatus($"{downloadCount} models completed...", ConsoleColor.DarkGreen);
}
}

ForegroundColor = ConsoleColor.Green;
WriteLine($"DONE.");
UpdateStatus("DONE!", ConsoleColor.White);

System.Console.Title = "Remix3D Downloader - Done!";

_cancellationTokenSource.Dispose();
_cancellationTokenSource = null;
Expand All @@ -127,8 +133,9 @@ private static async Task DownloadAllFilesAsync(IEnumerable<ModelResult> items,
break;
}

ForegroundColor = ConsoleColor.Yellow;
WriteLine($"Downloading {item.Name}...");
System.Console.Title = $"Remix3D Downloader - Downloading {item.Name}...";

UpdateStatus($"Downloading {item.Name}...", ConsoleColor.DarkYellow);

// *** Phase 1 - Always downloading the original model file *** //

Expand All @@ -140,35 +147,40 @@ private static async Task DownloadAllFilesAsync(IEnumerable<ModelResult> items,

if (string.IsNullOrEmpty(downloadUrl))
{
System.Console.SetCursorPosition(0, System.Console.CursorTop - 1);
ClearCurrentConsoleLine();

Debug.WriteLine($"{item.Name} downloadUrl was empty, skipping...");
continue;
}

using (var response = await _client.GetAsync(downloadUrl, _cancellationToken))
{
if (response.IsSuccessStatusCode)
if (!response.IsSuccessStatusCode)
{
var bytes = await response.Content.ReadAsByteArrayAsync();
System.Console.SetCursorPosition(0, System.Console.CursorTop - 1);
ClearCurrentConsoleLine();

if (bytes == null)
{
ForegroundColor = ConsoleColor.Red;
Debug.WriteLine($"{item.Name} was not downloaded.");
continue;
}
Debug.WriteLine($"Skipping {item.Name}, bad HTTP status code - {response.StatusCode}");
continue;
}

var bytes = await response.Content.ReadAsByteArrayAsync();

if (bytes == null)
{
UpdateStatus($"{item.Name} was not downloaded.", ConsoleColor.DarkRed);
continue;
}

var fileType = item.ManifestUris.FirstOrDefault(u => u.Usage == "Download")?.Format;
var fileType = item.ManifestUris.FirstOrDefault(u => u.Usage == "Download")?.Format;

var fileName = $"{item.Name}.{fileType}";
var filePath = Path.Combine(modelSubfolder.FullName, fileName);
var fileName = $"{item.Name}.{fileType}";
var filePath = Path.Combine(modelSubfolder.FullName, fileName);

File.WriteAllBytes(filePath, bytes);
File.WriteAllBytes(filePath, bytes);

ForegroundColor = ConsoleColor.White;
SetCursorPosition(CursorLeft, CursorTop - 1);
WriteLine();
SetCursorPosition(CursorLeft, CursorTop - 1);
WriteLine($"Saved {item.Name}.");
}
UpdateStatus($"Saved {item.Name}.", ConsoleColor.White, true);
}

// *** Phase 2 - Download all the optimized versions available *** //
Expand All @@ -187,8 +199,7 @@ private static async Task DownloadAllFilesAsync(IEnumerable<ModelResult> items,
break;
}

ForegroundColor = ConsoleColor.DarkGray;
WriteLine($"Downloading {optimization} version of {item.Name}...");
UpdateStatus($"Downloading {optimization} version of {item.Name}...", ConsoleColor.DarkYellow);

// Get the enum of the optimization type
var lod = (AssetOptimizationType)Enum.Parse(typeof(AssetOptimizationType), optimization);
Expand All @@ -197,23 +208,29 @@ private static async Task DownloadAllFilesAsync(IEnumerable<ModelResult> items,

if (string.IsNullOrEmpty(extraDownloadUrl))
{
System.Console.SetCursorPosition(0, System.Console.CursorTop - 1);
ClearCurrentConsoleLine();
continue;
}

using (var response = await _client.GetAsync(extraDownloadUrl, _cancellationToken))
{
if (!response.IsSuccessStatusCode)
{
System.Console.SetCursorPosition(0, System.Console.CursorTop - 1);
ClearCurrentConsoleLine();
continue;
}

var extraFileBytes = await response.Content.ReadAsByteArrayAsync();

// If this one failed, move on to next optimization

if (extraFileBytes == null || extraFileBytes.Length == 0)
{
ForegroundColor = ConsoleColor.Red;
Debug.WriteLine($"{item.Name} - {optimization} was not downloaded.");
System.Console.SetCursorPosition(0, System.Console.CursorTop - 1);
ClearCurrentConsoleLine();
Debug.WriteLine($"{item.Name} - {optimization} was not saved.");
continue;
}

Expand All @@ -230,45 +247,61 @@ private static async Task DownloadAllFilesAsync(IEnumerable<ModelResult> items,

File.WriteAllBytes(extraFilePath, extraFileBytes);

SetCursorPosition(CursorLeft, CursorTop - 1);
WriteLine();
SetCursorPosition(CursorLeft, CursorTop - 1);
ForegroundColor = ConsoleColor.Yellow;
WriteLine($"Saved {item.Name} for {optimization}.");
UpdateStatus($"Saved {item.Name} for {optimization}.", ConsoleColor.Yellow, true);
}
}
catch (Exception ex)
{
ForegroundColor = ConsoleColor.Red;
WriteLine($"Exception getting LOD {item.Name} {optimization} - {ex.Message}");
var errorMessage = $"Exception getting LOD {item.Name} {optimization} - {ex.Message}";

Debug.WriteLine($"Exception getting LOD {item.Name} {optimization} - {ex.Message}");
UpdateStatus(errorMessage, ConsoleColor.Red);
Debug.WriteLine(errorMessage);
}
}

ForegroundColor = ConsoleColor.DarkCyan;
WriteLine($"Completed {item.Name}");

UpdateStatus($"--- {item.Name} complete ---", ConsoleColor.DarkCyan);
}
catch (Exception ex)
{
ForegroundColor = ConsoleColor.Red;
WriteLine($"Exception getting {item.Name} - {ex.Message}");
var errorMessage = $"Exception getting {item.Name} - {ex.Message}";

Debug.WriteLine($"Exception getting {item.Name} - {ex.Message}");
UpdateStatus(errorMessage, ConsoleColor.Red);
Debug.WriteLine(errorMessage);
}
}
}

private static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
{
ForegroundColor = ConsoleColor.Red;
WriteLine($"Canceling...");
UpdateStatus("Requesting cancel, please wait...", ConsoleColor.Red);

_cancellationTokenSource.Cancel();

_hasMoreItems = false;
e.Cancel = true;

UpdateStatus("Cancel request complete.", ConsoleColor.White);
}

private static void UpdateStatus(string message, ConsoleColor textColor, bool replaceLastLine = false)
{
if (replaceLastLine)
{
System.Console.SetCursorPosition(0, System.Console.CursorTop - 1);
ClearCurrentConsoleLine();
}

System.Console.ForegroundColor = textColor;
System.Console.WriteLine(message);
}

// Credit https://stackoverflow.com/a/8946847/1406210
public static void ClearCurrentConsoleLine()
{
int currentLineCursor = System.Console.CursorTop;
System.Console.SetCursorPosition(0, System.Console.CursorTop);
System.Console.Write(new string(' ', System.Console.WindowWidth));
System.Console.SetCursorPosition(0, currentLineCursor);
}
}
}
Loading

0 comments on commit cb88963

Please sign in to comment.