Skip to content

Commit

Permalink
Add feature to parse year and id from folder name,
Browse files Browse the repository at this point in the history
currently only supported id are
Movie - imdb, tmdb, tvdb
TV - imdb,tmddb,tvdb
Multi - imdb, tvdb

Games are not supported, and Multi is not recommended.
  • Loading branch information
DineshSolanki committed Feb 10, 2024
1 parent 7389c33 commit 5196171
Show file tree
Hide file tree
Showing 8 changed files with 272 additions and 18 deletions.
5 changes: 5 additions & 0 deletions FoliCon/Models/Data/ParsedTitle.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using FoliCon.Models.Enums;

namespace FoliCon.Models.Data;

public record ParsedTitle(string Title, IdType IdType, string Id, int Year);
9 changes: 9 additions & 0 deletions FoliCon/Models/Enums/IdType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace FoliCon.Models.Enums;

public enum IdType
{
None,
Tvdb,
Tmdb,
Imdb
}
3 changes: 2 additions & 1 deletion FoliCon/Modules/IGDB/IgdbService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using FoliCon.Models.Constants;
using System.Text;
using FoliCon.Models.Constants;
using FoliCon.Models.Data;
using NLog;

Expand Down
5 changes: 5 additions & 0 deletions FoliCon/Modules/TMDB/TMDB.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ public Task<ResultResponse> SearchAsync(string query, string searchMode)
return _tmdbService.SearchAsync(query, searchMode);
}

public Task<ResultResponse> SearchAsync(ParsedTitle parsedTitle, string searchMode)
{
return _tmdbService.SearchWithParamsAsync(parsedTitle, searchMode);
}

/// <summary>
/// Searches TMDB media by ID as per media Type.
/// </summary>
Expand Down
195 changes: 193 additions & 2 deletions FoliCon/Modules/TMDB/TMDBService.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
using FoliCon.Models.Constants;
using System.Windows.Documents;
using FoliCon.Models.Constants;
using FoliCon.Models.Data;
using FoliCon.Models.Enums;
using NLog;
using TMDbLib.Objects.Find;
using Collection = TMDbLib.Objects.Collections.Collection;

namespace FoliCon.Modules.TMDB;

Expand Down Expand Up @@ -99,4 +103,191 @@ public async Task<ResultResponse> SearchAsync(string query, string searchMode)
};
return response;
}
}

public async Task<ResultResponse> SearchWithParamsAsync(ParsedTitle parsedTitle, string searchMode)
{
Logger.Debug("Searching for {ParsedTitle} in {SearchMode}", parsedTitle, searchMode);
object r = null;
var mediaType = "";
var query = parsedTitle.Title;
if (searchMode == MediaTypes.Movie)
{
if (query.ToLower(CultureInfo.InvariantCulture).Contains("collection"))
{
r = parsedTitle.IdType switch
{
IdType.None => await _serviceClient.SearchCollectionAsync(query),
IdType.Tvdb => GetMovieSearchContainer(
await _serviceClient.FindAsync(FindExternalSource.TvDb, parsedTitle.Id)),
IdType.Tmdb => GetCollectionSearchContainer(
await _serviceClient.GetCollectionAsync(Convert.ToInt32(parsedTitle.Id))),
IdType.Imdb => GetMovieSearchContainer(
await _serviceClient.FindAsync(FindExternalSource.Imdb, parsedTitle.Id)),
_ => await _serviceClient.SearchCollectionAsync(query)
};

mediaType = MediaTypes.Collection;
}
else
{
r = parsedTitle.IdType switch
{
IdType.None => parsedTitle.Year != 0
? await _serviceClient.SearchMovieAsync(query: query, year: parsedTitle.Year)
: await _serviceClient.SearchMovieAsync(query),
IdType.Tvdb => GetMovieSearchContainer(await _serviceClient.FindAsync(FindExternalSource.TvDb, parsedTitle.Id)),
IdType.Tmdb => GetMovieSearchContainer(await _serviceClient.GetMovieAsync(Convert.ToInt32(parsedTitle.Id))),
IdType.Imdb => await _serviceClient.FindAsync(FindExternalSource.Imdb, parsedTitle.Id),
_ => parsedTitle.Year != 0
? await _serviceClient.SearchMovieAsync(query: query, year: parsedTitle.Year)
: await _serviceClient.SearchMovieAsync(query)
};

mediaType = MediaTypes.Movie;
}
}
else if (searchMode == MediaTypes.Tv)
{
r = parsedTitle.IdType switch
{
IdType.None => parsedTitle.Year != 0
? await _serviceClient.SearchTvShowAsync(query: query, firstAirDateYear: parsedTitle.Year)
: await _serviceClient.SearchTvShowAsync(query),
IdType.Tvdb => GetTvSearchContainer(await _serviceClient.FindAsync(FindExternalSource.TvDb, parsedTitle.Id)),
IdType.Tmdb => GetTvSearchContainer(await _serviceClient.GetTvShowAsync(Convert.ToInt32(parsedTitle.Id))),
IdType.Imdb => GetTvSearchContainer(await _serviceClient.FindAsync(FindExternalSource.Imdb, parsedTitle.Id)),
_ => parsedTitle.Year != 0
? await _serviceClient.SearchTvShowAsync(query: query, firstAirDateYear: parsedTitle.Year)
: await _serviceClient.SearchTvShowAsync(query)
};
mediaType = MediaTypes.Tv;
}
else if (searchMode == MediaTypes.Mtv)
{
r = parsedTitle.IdType switch
{
IdType.None => parsedTitle.Year != 0
? await _serviceClient.SearchMultiAsync(query: query, year: parsedTitle.Year)
: await _serviceClient.SearchMultiAsync(query),
IdType.Tvdb => GetMultiSearchContainer(await _serviceClient.FindAsync(FindExternalSource.TvDb, parsedTitle.Id)),
IdType.Imdb => GetMultiSearchContainer(await _serviceClient.FindAsync(FindExternalSource.Imdb, parsedTitle.Id)),
_ => parsedTitle.Year != 0
? await _serviceClient.SearchMultiAsync(query: query, year: parsedTitle.Year)
: await _serviceClient.SearchMultiAsync(query)
};
mediaType = MediaTypes.Mtv;
}

var response = new ResultResponse
{
Result = r,
MediaType = mediaType
};
return response;
}

private static SearchContainer<SearchMovie> GetMovieSearchContainer(FindContainer findContainer)
{
return new SearchContainer<SearchMovie>
{
TotalResults = findContainer.MovieResults.Count,
Results = findContainer.MovieResults
};
}

private static SearchContainer<SearchMovie> GetMovieSearchContainer(Movie movie)
{
return new SearchContainer<SearchMovie>
{
TotalResults = 1,
Results = movie is null ? [] : [ConvertMovieToSearchMovie(movie)]
};
}

private static SearchContainer<SearchCollection> GetCollectionSearchContainer(Collection collection)
{
return new SearchContainer<SearchCollection>
{
TotalResults = 1,
Results = collection is null ? [] : [ConvertCollectionToSearchCollection(collection)]
};
}

private static SearchContainer<SearchTv> GetTvSearchContainer(FindContainer findContainer)
{
return new SearchContainer<SearchTv>
{
TotalResults = findContainer.TvResults.Count,
Results = findContainer.TvResults
};
}

private static SearchContainer<SearchTv> GetTvSearchContainer(TvShow tvShow)
{
var rd = tvShow;
return new SearchContainer<SearchTv>
{
TotalResults = 1,
Results = tvShow is null ? [] : [ConvertTvShowToSearchTv(tvShow)]
};
}

private static SearchContainer<dynamic> GetMultiSearchContainer(FindContainer findContainer)
{
dynamic result;
if (findContainer.MovieResults.Count != 0)
{
result = findContainer.MovieResults;
}
else if (findContainer.TvResults.Count != 0)
{
result = findContainer.TvResults;
}
else
{
result = new List();
}
return new SearchContainer<dynamic>
{
TotalResults = findContainer.MovieResults.Count + findContainer.TvResults.Count,
Results = result
};
}

private static SearchTv ConvertTvShowToSearchTv(TvShow tvShow)
{
return new SearchTv
{
Id = tvShow.Id,
Name = tvShow.Name,
FirstAirDate = tvShow.FirstAirDate,
Overview = tvShow.Overview,
PosterPath = tvShow.PosterPath,
VoteAverage = tvShow.VoteAverage
};
}

private static SearchMovie ConvertMovieToSearchMovie(Movie movie)
{
return new SearchMovie
{
Id = movie.Id,
Title = movie.Title,
ReleaseDate = movie.ReleaseDate,
Overview = movie.Overview,
PosterPath = movie.PosterPath,
VoteAverage = movie.VoteAverage
};
}

private static SearchCollection ConvertCollectionToSearchCollection(Collection collection)
{
return new SearchCollection
{
Id = collection.Id,
Name = collection.Name,
PosterPath = collection.PosterPath,
BackdropPath = collection.BackdropPath
};
}
}
9 changes: 8 additions & 1 deletion FoliCon/Modules/utils/DataUtils.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using NLog;
using FoliCon.Models.Data;
using FoliCon.Models.Enums;
using NLog;

namespace FoliCon.Modules.utils;

Expand Down Expand Up @@ -26,4 +28,9 @@ public static string FormatRating(double ratingInput)
Logger.Debug("End FormatRatingString() - Formatted Rating : {FormattedRatingValue}", formattedRatingValue);
return formattedRatingValue;
}

public static bool ShouldUseParsedTitle(ParsedTitle parsedTitle)
{
return parsedTitle != null && (parsedTitle.Year != 0 || (parsedTitle.IdType != IdType.None && parsedTitle.Id != "0"));
}
}
52 changes: 43 additions & 9 deletions FoliCon/Modules/utils/TitleCleaner.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using NLog;
using FoliCon.Models.Data;
using FoliCon.Models.Enums;
using NLog;
using Logger = NLog.Logger;

namespace FoliCon.Modules.utils;
Expand All @@ -21,26 +23,33 @@ public static string Clean(string title)
return cleanTitle;
}

public static ParsedTitle CleanAndParse(string title)
{
var (normalizedTitle, idType, showId, year) = ExtractShowIdAndYear(title);
normalizedTitle = Clean(normalizedTitle);
return new ParsedTitle(normalizedTitle, idType, showId, year);
}

private static string NormalizeTitle(string title)
{
return title.Replace('-', ' ').Replace('_', ' ').Replace('.', ' ');
}

private static string CleanTitle(string title)
{
// \s* --Remove any whitespace which would be left at the end after this substitution
// \(? --Remove optional bracket starting (720p)
// (\d{4}) --Remove year from movie
// (420)|(720)|(1080) resolutions
// (year|resolutions) find at least one main token to remove
// p?i? \)? --Not needed. To emphasize removal of 1080i, closing bracket etc, but not needed due to the last part
// .* --Remove all trailing information after having found year or resolution as junk usually follows
/*\s* --Remove any whitespace which would be left at the end after this substitution
\(? --Remove optional bracket starting (720p)
(\d{4}) --Remove year from movie
(420)|(720)|(1080) resolutions
(year|resolutions) find at least one main token to remove
p?i? \)? --Not needed. To emphasize removal of 1080i, closing bracket etc, but not needed due to the last part
.* --Remove all trailing information after having found year or resolution as junk usually follows*/

var cleanTitle = Regex.Replace(title, "\\s*\\(?((\\d{4})|(420)|(720)|(1080))p?i?\\)?.*", "", RegexOptions.IgnoreCase | RegexOptions.Compiled);
cleanTitle = Regex.Replace(cleanTitle, @"\[.*\]", "", RegexOptions.IgnoreCase | RegexOptions.Compiled);
cleanTitle = Regex.Replace(cleanTitle, " {2,}", " ", RegexOptions.IgnoreCase | RegexOptions.Compiled);

return string.IsNullOrWhiteSpace(cleanTitle) ? title : cleanTitle;
return string.IsNullOrWhiteSpace(cleanTitle) ? title.Trim() : cleanTitle.Trim();
}

private static string RemoveReplaceUnicodeCharacters(string title)
Expand All @@ -50,4 +59,29 @@ private static string RemoveReplaceUnicodeCharacters(string title)
// Remove other remaining unicode characters
return Regex.Replace(title, @"[^\u0000-\u007F]+", string.Empty);
}

private static (string, IdType, string, int) ExtractShowIdAndYear(string title)
{
const string showIdPattern = @"\{(tvdb|tmdb)-(\d+)\}";
const string yearPattern = @"\((\d{4})\)";

var showIdMatch = Regex.Match(title, showIdPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);
var yearMatch = Regex.Match(title, yearPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);

var showIdType = IdType.None;
var showId = "0";

if (showIdMatch.Success)
{
showIdType = Enum.TryParse(showIdMatch.Groups[1].Value, true, out IdType parsedShowIdType) ? parsedShowIdType : IdType.None;
showId = showIdMatch.Groups[2].Value;
title = Regex.Replace(title, showIdPattern, "", RegexOptions.IgnoreCase | RegexOptions.Compiled);
}

if (!yearMatch.Success) return (title, showIdType, showId, 0);
var year = Convert.ToInt32(yearMatch.Groups[1].Value);
title = Regex.Replace(title, yearPattern, "", RegexOptions.IgnoreCase | RegexOptions.Compiled);

return (title, showIdType, showId, year);
}
}
12 changes: 7 additions & 5 deletions FoliCon/ViewModels/MainWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ private async Task ProcessPosterModeAsync()
StatusBarProperties.AppStatusAdditional = itemTitle;
// TODO: Set cursor to WAIT.
var isAutoPicked = false;
var searchTitle = TitleCleaner.Clean(itemTitle);
var parsedTitle = TitleCleaner.CleanAndParse(itemTitle);
var (id, mediaType) = FileUtils.ReadMediaInfo(fullFolderPath);
var isPickedById = false;
ResultResponse response;
Expand All @@ -416,8 +416,10 @@ private async Task ProcessPosterModeAsync()
{
Logger.Info("MediaInfo not found for {ItemTitle}, Searching by Title", itemTitle);
response = SearchMode == "Game"
? await _igdbObject.SearchGameAsync(searchTitle)
: await _tmdbObject.SearchAsync(searchTitle, SearchMode);
? await _igdbObject.SearchGameAsync(parsedTitle.Title)
: DataUtils.ShouldUseParsedTitle(parsedTitle)
? await _tmdbObject.SearchAsync(parsedTitle, SearchMode)
: await _tmdbObject.SearchAsync(parsedTitle.Title, SearchMode);
}
int resultCount = isPickedById ? response.Result != null ? 1 : 0 : SearchMode == "Game" ? response.Result.Length : response.Result.TotalResults;
Logger.Info("Search Result Count: {ResultCount}", resultCount);
Expand All @@ -427,7 +429,7 @@ private async Task ProcessPosterModeAsync()
Logger.Debug("No result found for {ItemTitle}, {Mode}", itemTitle, SearchMode);
MessageBox.Show(CustomMessageBox.Info(LangProvider.GetLang("NothingFoundFor").Format(itemTitle),
LangProvider.GetLang("NoResultFound")));
_dialogService.ShowSearchResult(SearchMode, searchTitle, fullFolderPath, response,
_dialogService.ShowSearchResult(SearchMode, parsedTitle.Title, fullFolderPath, response,
_tmdbObject, _igdbObject, isPickedById,
r =>
{
Expand Down Expand Up @@ -488,7 +490,7 @@ private async Task ProcessPosterModeAsync()
"always show poster window: {IsPosterWindowShown}, Skip ambigous titles: {IsSkipAmbiguous}," +
" showing poster window", itemTitle, SearchMode, IsPosterWindowShown, IsSkipAmbiguous);

_dialogService.ShowSearchResult(SearchMode, searchTitle, fullFolderPath,
_dialogService.ShowSearchResult(SearchMode, parsedTitle.Title, fullFolderPath,
response, _tmdbObject, _igdbObject, isPickedById,
r =>
{
Expand Down

0 comments on commit 5196171

Please sign in to comment.