initial commit
This commit is contained in:
BIN
.vs/LetterBoxdUnwatchedGenerator/DesignTimeBuild/.dtbcache.v2
Normal file
BIN
.vs/LetterBoxdUnwatchedGenerator/DesignTimeBuild/.dtbcache.v2
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
.vs/LetterBoxdUnwatchedGenerator/v17/.suo
Normal file
BIN
.vs/LetterBoxdUnwatchedGenerator/v17/.suo
Normal file
Binary file not shown.
25
LetterBoxdUnwatchedGenerator.sln
Normal file
25
LetterBoxdUnwatchedGenerator.sln
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.6.33723.286
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LetterBoxdUnwatchedGenerator", "LetterBoxdUnwatchedGenerator\LetterBoxdUnwatchedGenerator.csproj", "{AA177F53-376A-40C9-B372-BA5FD25FEAD7}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{AA177F53-376A-40C9-B372-BA5FD25FEAD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{AA177F53-376A-40C9-B372-BA5FD25FEAD7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{AA177F53-376A-40C9-B372-BA5FD25FEAD7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{AA177F53-376A-40C9-B372-BA5FD25FEAD7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {F57C2260-910C-480D-A2FF-22FF5A6EEDE7}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
91
LetterBoxdUnwatchedGenerator/CommandLineSplit.cs
Normal file
91
LetterBoxdUnwatchedGenerator/CommandLineSplit.cs
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace LetterBoxdUnwatchedGenerator
|
||||||
|
{
|
||||||
|
internal static class CommandLineSplitExt
|
||||||
|
{
|
||||||
|
|
||||||
|
//public static IEnumerable<string> SplitCommandLine(this string commandLine, char splitChar = ' ')
|
||||||
|
//{
|
||||||
|
// bool inQuotes = false;
|
||||||
|
// bool isEscaping = false;
|
||||||
|
|
||||||
|
// StringBuilder currentArg = new StringBuilder();
|
||||||
|
|
||||||
|
// foreach (char c in commandLine)
|
||||||
|
// {
|
||||||
|
// if (c == '\\' && !isEscaping) { isEscaping = true; continue; }
|
||||||
|
// if (c == '\"' && !isEscaping) inQuotes = !inQuotes;
|
||||||
|
// isEscaping = false;
|
||||||
|
|
||||||
|
// if (!inQuotes && (splitChar == ' ' ? Char.IsWhiteSpace(c) : c == splitChar))
|
||||||
|
// {
|
||||||
|
// if (currentArg.Length > 0)
|
||||||
|
// {
|
||||||
|
// yield return currentArg.ToString();
|
||||||
|
// currentArg.Clear();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// currentArg.Append(c);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (currentArg.Length > 0)
|
||||||
|
// {
|
||||||
|
// yield return currentArg.ToString();
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
public static IEnumerable<string> SplitCommandLine(this string commandLine, char splitChar = ' ')
|
||||||
|
{
|
||||||
|
bool inQuotes = false;
|
||||||
|
bool isEscaping = false;
|
||||||
|
|
||||||
|
return commandLine.Split(c =>
|
||||||
|
{
|
||||||
|
if (c == '\\' && !isEscaping) { isEscaping = true; return false; }
|
||||||
|
if (c == '\"' && !isEscaping) inQuotes = !inQuotes;
|
||||||
|
isEscaping = false;
|
||||||
|
|
||||||
|
return !inQuotes && (splitChar == ' ' ? Char.IsWhiteSpace(c) : c == splitChar); //Char.IsWhiteSpace(c)/*c == ' '*/;
|
||||||
|
}).Select(arg => arg.Trim().TrimMatchingQuotes('\"').Replace("\\\"", "\""));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<string> Split(this string str, Func<char, bool> controller)
|
||||||
|
{
|
||||||
|
int nextPiece = 0;
|
||||||
|
|
||||||
|
for (int c = 0; c < str.Length; c++)
|
||||||
|
{
|
||||||
|
if (controller(str[c]))
|
||||||
|
{
|
||||||
|
yield return str.Substring(nextPiece, c - nextPiece);
|
||||||
|
nextPiece = c + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
yield return str.Substring(nextPiece);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string TrimMatchingQuotes(this string input, char quote)
|
||||||
|
{
|
||||||
|
if ((input.Length >= 2) &&
|
||||||
|
(input[0] == quote) && (input[input.Length - 1] == quote))
|
||||||
|
return input.Substring(1, input.Length - 2);
|
||||||
|
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string JoinCommandLine(this IEnumerable<string> arguments, char joinChar = ' ')
|
||||||
|
{
|
||||||
|
return string.Join(joinChar, arguments.Select(a => a.Contains(joinChar) ? $"\"{a}\"" : a));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
8
LetterBoxdUnwatchedGenerator/IMovie.cs
Normal file
8
LetterBoxdUnwatchedGenerator/IMovie.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace LetterBoxdUnwatchedGenerator
|
||||||
|
{
|
||||||
|
public interface IMovie
|
||||||
|
{
|
||||||
|
public string Title { get; }
|
||||||
|
public int Year { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
25
LetterBoxdUnwatchedGenerator/LetterBoxdConnection.cs
Normal file
25
LetterBoxdUnwatchedGenerator/LetterBoxdConnection.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using System.Net.Http.Headers;
|
||||||
|
|
||||||
|
namespace LetterBoxdUnwatchedGenerator
|
||||||
|
{
|
||||||
|
public class LetterBoxdConnection
|
||||||
|
{
|
||||||
|
const string baseUrl = "https://letterboxd.com/api/v0/";
|
||||||
|
HttpClient httpClient;
|
||||||
|
|
||||||
|
public LetterBoxdConnection()
|
||||||
|
{
|
||||||
|
//Authentication
|
||||||
|
httpClient = new HttpClient();
|
||||||
|
httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
|
||||||
|
//add url form encoded content
|
||||||
|
var results = httpClient.PostAsync(baseUrl + "auth/token", new StringContent("grant_type=password&username=USERNAME&password=PASSWORD", MediaTypeHeaderValue.Parse("application/x-www-form-urlencoded"))); //, Encoding.UTF8, "application/x-www-form-urlencoded"));
|
||||||
|
|
||||||
|
results.Result.EnsureSuccessStatusCode();
|
||||||
|
var bearer = results.Result.Content.ReadAsStringAsync().Result;
|
||||||
|
|
||||||
|
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", bearer);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
37
LetterBoxdUnwatchedGenerator/LetterBoxdWatchedMovie.cs
Normal file
37
LetterBoxdUnwatchedGenerator/LetterBoxdWatchedMovie.cs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
namespace LetterBoxdUnwatchedGenerator
|
||||||
|
{
|
||||||
|
public class LetterBoxdWatchedMovie : IMovie
|
||||||
|
{
|
||||||
|
public DateTime Date { get; }
|
||||||
|
public string Title { get; }
|
||||||
|
public int Year { get; }
|
||||||
|
public string LetterboxdUri { get; }
|
||||||
|
|
||||||
|
public LetterBoxdWatchedMovie(DateTime date, string title, int year, string letterboxdUri)
|
||||||
|
{
|
||||||
|
Date = date;
|
||||||
|
Title = title;
|
||||||
|
Year = year;
|
||||||
|
LetterboxdUri = letterboxdUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<LetterBoxdWatchedMovie> FromFile(string path)
|
||||||
|
{
|
||||||
|
var lines = File.ReadAllLines(path);
|
||||||
|
|
||||||
|
if (lines.Length < 1 || !lines[0].StartsWith("Date,Name,Year,Letterboxd URI")) return Enumerable.Empty<LetterBoxdWatchedMovie>();
|
||||||
|
|
||||||
|
return lines.Skip(1).Select(line =>
|
||||||
|
{
|
||||||
|
var lineParts = line.SplitCommandLine(',');
|
||||||
|
|
||||||
|
var date = !string.IsNullOrEmpty(lineParts.First()) && DateTime.TryParse(lineParts.First(), out DateTime dateOut) ? dateOut : DateTime.MinValue;
|
||||||
|
var title = lineParts.Skip(1).First();
|
||||||
|
var year = !string.IsNullOrEmpty(lineParts.Skip(2).First()) && int.TryParse(lineParts.Skip(2).First(), out int yearOut) ? yearOut : 0;
|
||||||
|
var letterboxdUri = lineParts.Skip(3).First();
|
||||||
|
|
||||||
|
return new LetterBoxdWatchedMovie(date, title, year, letterboxdUri);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
58
LetterBoxdUnwatchedGenerator/LocalMovie.cs
Normal file
58
LetterBoxdUnwatchedGenerator/LocalMovie.cs
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
namespace LetterBoxdUnwatchedGenerator
|
||||||
|
{
|
||||||
|
public class LocalMovie : IMovie
|
||||||
|
{
|
||||||
|
public string Title { get; }
|
||||||
|
public int Year { get; }
|
||||||
|
public string? Resolution { get; }
|
||||||
|
public string Directory { get; }
|
||||||
|
|
||||||
|
public LocalMovie(string title, int year, string? resolution, string directory)
|
||||||
|
{
|
||||||
|
Title = title;
|
||||||
|
Year = year;
|
||||||
|
Resolution = resolution;
|
||||||
|
Directory = directory;
|
||||||
|
}
|
||||||
|
public static LocalMovie FromDirectory(string directoryName, string directoryFullName)
|
||||||
|
{
|
||||||
|
string title;
|
||||||
|
int? year;
|
||||||
|
string? resolution;
|
||||||
|
|
||||||
|
var yearIndex = directoryName.LastIndexOf('(');
|
||||||
|
var resolutionIndex = directoryName.LastIndexOf('[');
|
||||||
|
if (yearIndex == -1)
|
||||||
|
{
|
||||||
|
year = null;
|
||||||
|
if (resolutionIndex == -1)
|
||||||
|
{
|
||||||
|
resolution = null;
|
||||||
|
title = directoryName;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
resolution = directoryName.Substring(resolutionIndex + 1, directoryName.LastIndexOf(']') - resolutionIndex - 1);
|
||||||
|
title = directoryName[..(resolutionIndex - 1)].Trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (int.TryParse(directoryName.AsSpan(yearIndex + 1, directoryName.LastIndexOf(')') - yearIndex - 1), out int yearConverted)) year = yearConverted;
|
||||||
|
else year = null;
|
||||||
|
if (resolutionIndex == -1)
|
||||||
|
{
|
||||||
|
resolution = null;
|
||||||
|
title = directoryName[..(yearIndex - 1)].Trim();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
resolution = directoryName.Substring(resolutionIndex + 1, directoryName.LastIndexOf(']') - resolutionIndex - 1);
|
||||||
|
title = directoryName[..(yearIndex - 1)].Trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new LocalMovie(title, year ?? 0, resolution, directoryFullName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
39
LetterBoxdUnwatchedGenerator/Program.cs
Normal file
39
LetterBoxdUnwatchedGenerator/Program.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace LetterBoxdUnwatchedGenerator
|
||||||
|
{
|
||||||
|
internal class Program
|
||||||
|
{
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
DirectoryInfo di = new DirectoryInfo(@"A:\Video\Movies\All");
|
||||||
|
var localMovies = di.EnumerateDirectories().Select(di => LocalMovie.FromDirectory(di.Name, di.FullName)).ToList();
|
||||||
|
|
||||||
|
var watchedMovies = LetterBoxdWatchedMovie.FromFile(@"A:\homes\@DH-ARNEHOME\0\arne-1103\Documents\Inactive\2023-07-01 Letterboxd download\letterboxd-thearne4-2023-07-01-10-32-utc\watched.csv");
|
||||||
|
|
||||||
|
var unwatchedMovies = localMovies.Where(lm => !watchedMovies.Any(wm => lm.Title.Replace("-", "").Replace("(", "").Replace(")", "").Replace(" ", "").ToLowerInvariant().EndsWith(wm.Title.Replace("\\", "").Replace("/", "").Replace(":", "").Replace("\"", "").Replace("?", "").Replace("<", "").Replace(">", "").Replace("|", "").Replace("-", "").Replace(" ", "").ToLowerInvariant()) && wm.Year == lm.Year));
|
||||||
|
|
||||||
|
WriteToCsv(@"A:\homes\@DH-ARNEHOME\0\arne-1103\Documents\Inactive\2023-07-01 Letterboxd download\local-unwatched.csv", unwatchedMovies);
|
||||||
|
|
||||||
|
//unwatchedMovies.ForEach(um => Console.WriteLine(um.Title));
|
||||||
|
|
||||||
|
Console.WriteLine("-------------");
|
||||||
|
Console.WriteLine($"Total: {unwatchedMovies.Count()}");
|
||||||
|
Console.ReadKey();
|
||||||
|
}
|
||||||
|
static void WriteToCsv(string path, IEnumerable<IMovie> unwatchedMovies)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new();
|
||||||
|
sb.AppendLine("Title,Year");
|
||||||
|
foreach (IMovie movie in unwatchedMovies)
|
||||||
|
{
|
||||||
|
var title = movie.Title.Contains(',') ? $"\"{movie.Title}\"" : movie.Title;
|
||||||
|
var year = movie.Year == 0 ? "" : movie.Year.ToString();
|
||||||
|
|
||||||
|
sb.AppendLine($"{title},{year}");
|
||||||
|
}
|
||||||
|
|
||||||
|
File.WriteAllText(path, sb.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user