commit c91bde761ee4aa288b814253334ca1c6d0a5d7e7 Author: Amir.H Khademi Date: Fri Jan 15 18:13:54 2021 +0330 Add project files. diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4ce6fdd --- /dev/null +++ b/.gitignore @@ -0,0 +1,340 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- Backup*.rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb \ No newline at end of file diff --git a/Plix.sln b/Plix.sln new file mode 100644 index 0000000..30fbc5f --- /dev/null +++ b/Plix.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29613.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlixP", "PlixP\PlixP.csproj", "{D97DA4A8-8F8C-4A43-A868-69912C04BEE7}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D97DA4A8-8F8C-4A43-A868-69912C04BEE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D97DA4A8-8F8C-4A43-A868-69912C04BEE7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D97DA4A8-8F8C-4A43-A868-69912C04BEE7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D97DA4A8-8F8C-4A43-A868-69912C04BEE7}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {17816672-B5D2-4079-83EA-D88ABDD69DEE} + EndGlobalSection +EndGlobal diff --git a/Plix.sln.DotSettings b/Plix.sln.DotSettings new file mode 100644 index 0000000..1de041c --- /dev/null +++ b/Plix.sln.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/PlixLogo.ico b/PlixLogo.ico new file mode 100644 index 0000000..4ffdaf5 Binary files /dev/null and b/PlixLogo.ico differ diff --git a/PlixLogo.png b/PlixLogo.png new file mode 100644 index 0000000..242403f Binary files /dev/null and b/PlixLogo.png differ diff --git a/PlixP/App.xaml b/PlixP/App.xaml new file mode 100644 index 0000000..ae2ac54 --- /dev/null +++ b/PlixP/App.xaml @@ -0,0 +1,44 @@ + + + + + + + + + + String 1A + String 1B + String 1C + String 1A + String 1B + String 1C + + + + + + + + + + + + + + + diff --git a/PlixP/App.xaml.cs b/PlixP/App.xaml.cs new file mode 100644 index 0000000..d2e8394 --- /dev/null +++ b/PlixP/App.xaml.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Prism.Ioc; +using PlixP.Views; +using System.Windows; +using AutoMapper; +using Microsoft.EntityFrameworkCore; +using PlixP.Extentions; +using PlixP.Models; +using PlixP.Repositories; +using PlixP.Repositories.Contracts; +using PlixP.Services; +using PlixP.Services.Contracts; + +namespace PlixP +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App + { + protected override Window CreateShell() + { + var _catcherService = new CatcherService(); + _catcherService.Delete>(CatcherKeys.MovieList); + using (var _context = new PlixContext()) + { + _context.Database.Migrate(); + if (_context.Categories.Count() == 0) + { + _context.Categories.Add(new Category + { + Name = "Uncategorized" + }); + _context.SaveChanges(); + } + } + + AutoMapperConfig.ConfigurationMapper(); + App.Current.ConfigureExceptionHandling(AppDomain.CurrentDomain); + MovieServiceRealTime.Instance.StartReal(); + return Container.Resolve(); + } + + + protected override void RegisterTypes(IContainerRegistry containerRegistry) + { + containerRegistry.RegisterForNavigation(); + containerRegistry.Register(); + containerRegistry.Register(); + } + } +} diff --git a/PlixP/Extentions/Assert.cs b/PlixP/Extentions/Assert.cs new file mode 100644 index 0000000..ad56c07 --- /dev/null +++ b/PlixP/Extentions/Assert.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PlixP.Extentions +{ + public static class Assert + { + public static void NotNull(T obj, string name, string message = null) + where T : class + { + if (obj is null) + throw new ArgumentNullException($"{name} : {typeof(T)}", message); + } + + public static void NotNull(T? obj, string name, string message = null) + where T : struct + { + if (!obj.HasValue) + throw new ArgumentNullException($"{name} : {typeof(T)}", message); + + } + + public static void NotEmpty(T obj, string name, string message = null, T defaultValue = null) + where T : class + { + if (obj == defaultValue + || (obj is string str && string.IsNullOrWhiteSpace(str)) + || (obj is IEnumerable list && !list.Cast().Any())) + { + throw new ArgumentException("Argument is empty : " + message, $"{name} : {typeof(T)}"); + } + } + } +} diff --git a/PlixP/Extentions/AutoMapperConfig.cs b/PlixP/Extentions/AutoMapperConfig.cs new file mode 100644 index 0000000..af34652 --- /dev/null +++ b/PlixP/Extentions/AutoMapperConfig.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using AutoMapper; +using PlixP.Models; + +namespace PlixP.Extentions +{ + public static class AutoMapperConfig + { + public static void ConfigurationMapper() + { + Mapper.Initialize(config => { config.AddCustomMapping(Assembly.GetEntryAssembly()); }); + Mapper.Configuration.CompileMappings(); + } + + public static void AddCustomMapping(this IMapperConfigurationExpression expression, Assembly assemblies) + { + var allTypes = assemblies.ExportedTypes; + var list = allTypes + .Where(type => type.IsClass && !type.IsAbstract && type.GetInterfaces().Contains(typeof(IMapperConf))) + .Select(type => (IMapperConf)Activator.CreateInstance(type)).ToList(); + expression.AddProfile(new CustomMappingProfile(list)); + } + } +} diff --git a/PlixP/Extentions/WpfExtensions.cs b/PlixP/Extentions/WpfExtensions.cs new file mode 100644 index 0000000..38d806f --- /dev/null +++ b/PlixP/Extentions/WpfExtensions.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Media; +using MessageBox = HandyControl.Controls.MessageBox; + +namespace PlixP.Extentions +{ + public static class WpfExtensions + { + public static Brush FromHex(string hexColor) + { + return (SolidColorBrush)(new BrushConverter().ConvertFrom(hexColor)); + } + + public static void ConfigureExceptionHandling(this Application application, AppDomain appDomain) + { + application.Dispatcher.UnhandledException += Dispatcher_UnhandledException; + application.DispatcherUnhandledException += Application_DispatcherUnhandledException; + appDomain.UnhandledException += AppDomain_UnhandledException; + } + + private static void AppDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) + { + MessageBox.Show(e.ExceptionObject.ToString()); + } + + private static void Application_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e) + { + if (e.Handled == false) + { + MessageBox.Show(e.Exception.Message); + e.Handled = true; + } + } + + private static void Dispatcher_UnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e) + { + if (e.Handled == false) + { + MessageBox.Show(e.Exception.Message); + e.Handled = true; + } + } + } +} diff --git a/PlixP/FodyWeavers.xml b/PlixP/FodyWeavers.xml new file mode 100644 index 0000000..d5abfed --- /dev/null +++ b/PlixP/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/PlixP/FodyWeavers.xsd b/PlixP/FodyWeavers.xsd new file mode 100644 index 0000000..69dbe48 --- /dev/null +++ b/PlixP/FodyWeavers.xsd @@ -0,0 +1,74 @@ + + + + + + + + + + + Used to control if the On_PropertyName_Changed feature is enabled. + + + + + Used to control if the Dependent properties feature is enabled. + + + + + Used to control if the IsChanged property feature is enabled. + + + + + Used to change the name of the method that fires the notify event. This is a string that accepts multiple values in a comma separated form. + + + + + Used to control if equality checks should be inserted. If false, equality checking will be disabled for the project. + + + + + Used to control if equality checks should use the Equals method resolved from the base class. + + + + + Used to control if equality checks should use the static Equals method resolved from the base class. + + + + + Used to turn off build warnings from this weaver. + + + + + Used to turn off build warnings about mismatched On_PropertyName_Changed methods. + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/PlixP/Migrations/20201219204740_init.Designer.cs b/PlixP/Migrations/20201219204740_init.Designer.cs new file mode 100644 index 0000000..82154b6 --- /dev/null +++ b/PlixP/Migrations/20201219204740_init.Designer.cs @@ -0,0 +1,244 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using PlixP.Models; + +namespace PlixP.Migrations +{ + [DbContext(typeof(PlixContext))] + [Migration("20201219204740_init")] + partial class init + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.1"); + + modelBuilder.Entity("PlixP.Models.Category", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("IsRemoved") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Categories"); + }); + + modelBuilder.Entity("PlixP.Models.CategoryMovie", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CategoryId") + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("IsRemoved") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CategoryId"); + + b.HasIndex("MovieId"); + + b.ToTable("CategoryMovies"); + }); + + modelBuilder.Entity("PlixP.Models.Movie", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Actors") + .HasColumnType("TEXT"); + + b.Property("Awards") + .HasColumnType("TEXT"); + + b.Property("BoxOffice") + .HasColumnType("TEXT"); + + b.Property("Country") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("DVD") + .HasColumnType("TEXT"); + + b.Property("Director") + .HasColumnType("TEXT"); + + b.Property("FileName") + .HasColumnType("TEXT"); + + b.Property("Genre") + .HasColumnType("TEXT"); + + b.Property("IsRemoved") + .HasColumnType("INTEGER"); + + b.Property("IsSeen") + .HasColumnType("INTEGER"); + + b.Property("Language") + .HasColumnType("TEXT"); + + b.Property("Location") + .HasColumnType("TEXT"); + + b.Property("Metascore") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("Production") + .HasColumnType("TEXT"); + + b.Property("Quality") + .HasColumnType("TEXT"); + + b.Property("Rated") + .HasColumnType("TEXT"); + + b.Property("Released") + .HasColumnType("TEXT"); + + b.Property("Response") + .HasColumnType("TEXT"); + + b.Property("Runtime") + .HasColumnType("TEXT"); + + b.Property("SyncStatus") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("TEXT"); + + b.Property("Website") + .HasColumnType("TEXT"); + + b.Property("Writer") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("TEXT"); + + b.Property("imdbID") + .HasColumnType("TEXT"); + + b.Property("imdbRating") + .HasColumnType("TEXT"); + + b.Property("imdbVotes") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Movies"); + }); + + modelBuilder.Entity("PlixP.Models.Rating", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("IsRemoved") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("Source") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("Ratings"); + }); + + modelBuilder.Entity("PlixP.Models.CategoryMovie", b => + { + b.HasOne("PlixP.Models.Category", "Category") + .WithMany("Movies") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("PlixP.Models.Movie", "Movie") + .WithMany("Category") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("PlixP.Models.Rating", b => + { + b.HasOne("PlixP.Models.Movie", "Movie") + .WithMany("Ratings") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("PlixP.Models.Category", b => + { + b.Navigation("Movies"); + }); + + modelBuilder.Entity("PlixP.Models.Movie", b => + { + b.Navigation("Category"); + + b.Navigation("Ratings"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/PlixP/Migrations/20201219204740_init.cs b/PlixP/Migrations/20201219204740_init.cs new file mode 100644 index 0000000..561c4c3 --- /dev/null +++ b/PlixP/Migrations/20201219204740_init.cs @@ -0,0 +1,150 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace PlixP.Migrations +{ + public partial class init : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Categories", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column(type: "TEXT", nullable: true), + IsRemoved = table.Column(type: "INTEGER", nullable: false), + CreationDate = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Categories", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Movies", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + FileName = table.Column(type: "TEXT", nullable: true), + Location = table.Column(type: "TEXT", nullable: true), + Quality = table.Column(type: "TEXT", nullable: true), + Title = table.Column(type: "TEXT", nullable: true), + Year = table.Column(type: "TEXT", nullable: true), + Rated = table.Column(type: "TEXT", nullable: true), + Released = table.Column(type: "TEXT", nullable: true), + Runtime = table.Column(type: "TEXT", nullable: true), + Genre = table.Column(type: "TEXT", nullable: true), + Director = table.Column(type: "TEXT", nullable: true), + Writer = table.Column(type: "TEXT", nullable: true), + Actors = table.Column(type: "TEXT", nullable: true), + Plot = table.Column(type: "TEXT", nullable: true), + Language = table.Column(type: "TEXT", nullable: true), + Country = table.Column(type: "TEXT", nullable: true), + Awards = table.Column(type: "TEXT", nullable: true), + Poster = table.Column(type: "TEXT", nullable: true), + Metascore = table.Column(type: "TEXT", nullable: true), + imdbRating = table.Column(type: "TEXT", nullable: true), + imdbVotes = table.Column(type: "TEXT", nullable: true), + imdbID = table.Column(type: "TEXT", nullable: true), + Type = table.Column(type: "TEXT", nullable: true), + DVD = table.Column(type: "TEXT", nullable: true), + BoxOffice = table.Column(type: "TEXT", nullable: true), + Production = table.Column(type: "TEXT", nullable: true), + Website = table.Column(type: "TEXT", nullable: true), + Response = table.Column(type: "TEXT", nullable: true), + IsSeen = table.Column(type: "INTEGER", nullable: false), + SyncStatus = table.Column(type: "INTEGER", nullable: false), + IsRemoved = table.Column(type: "INTEGER", nullable: false), + CreationDate = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Movies", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "CategoryMovies", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + CategoryId = table.Column(type: "INTEGER", nullable: false), + MovieId = table.Column(type: "INTEGER", nullable: false), + IsRemoved = table.Column(type: "INTEGER", nullable: false), + CreationDate = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_CategoryMovies", x => x.Id); + table.ForeignKey( + name: "FK_CategoryMovies_Categories_CategoryId", + column: x => x.CategoryId, + principalTable: "Categories", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_CategoryMovies_Movies_MovieId", + column: x => x.MovieId, + principalTable: "Movies", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Ratings", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Source = table.Column(type: "TEXT", nullable: true), + Value = table.Column(type: "TEXT", nullable: true), + MovieId = table.Column(type: "INTEGER", nullable: false), + IsRemoved = table.Column(type: "INTEGER", nullable: false), + CreationDate = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Ratings", x => x.Id); + table.ForeignKey( + name: "FK_Ratings_Movies_MovieId", + column: x => x.MovieId, + principalTable: "Movies", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_CategoryMovies_CategoryId", + table: "CategoryMovies", + column: "CategoryId"); + + migrationBuilder.CreateIndex( + name: "IX_CategoryMovies_MovieId", + table: "CategoryMovies", + column: "MovieId"); + + migrationBuilder.CreateIndex( + name: "IX_Ratings_MovieId", + table: "Ratings", + column: "MovieId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "CategoryMovies"); + + migrationBuilder.DropTable( + name: "Ratings"); + + migrationBuilder.DropTable( + name: "Categories"); + + migrationBuilder.DropTable( + name: "Movies"); + } + } +} diff --git a/PlixP/Migrations/PlixContextModelSnapshot.cs b/PlixP/Migrations/PlixContextModelSnapshot.cs new file mode 100644 index 0000000..dda0711 --- /dev/null +++ b/PlixP/Migrations/PlixContextModelSnapshot.cs @@ -0,0 +1,242 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using PlixP.Models; + +namespace PlixP.Migrations +{ + [DbContext(typeof(PlixContext))] + partial class PlixContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "5.0.1"); + + modelBuilder.Entity("PlixP.Models.Category", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("IsRemoved") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Categories"); + }); + + modelBuilder.Entity("PlixP.Models.CategoryMovie", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CategoryId") + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("IsRemoved") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CategoryId"); + + b.HasIndex("MovieId"); + + b.ToTable("CategoryMovies"); + }); + + modelBuilder.Entity("PlixP.Models.Movie", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Actors") + .HasColumnType("TEXT"); + + b.Property("Awards") + .HasColumnType("TEXT"); + + b.Property("BoxOffice") + .HasColumnType("TEXT"); + + b.Property("Country") + .HasColumnType("TEXT"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("DVD") + .HasColumnType("TEXT"); + + b.Property("Director") + .HasColumnType("TEXT"); + + b.Property("FileName") + .HasColumnType("TEXT"); + + b.Property("Genre") + .HasColumnType("TEXT"); + + b.Property("IsRemoved") + .HasColumnType("INTEGER"); + + b.Property("IsSeen") + .HasColumnType("INTEGER"); + + b.Property("Language") + .HasColumnType("TEXT"); + + b.Property("Location") + .HasColumnType("TEXT"); + + b.Property("Metascore") + .HasColumnType("TEXT"); + + b.Property("Plot") + .HasColumnType("TEXT"); + + b.Property("Poster") + .HasColumnType("TEXT"); + + b.Property("Production") + .HasColumnType("TEXT"); + + b.Property("Quality") + .HasColumnType("TEXT"); + + b.Property("Rated") + .HasColumnType("TEXT"); + + b.Property("Released") + .HasColumnType("TEXT"); + + b.Property("Response") + .HasColumnType("TEXT"); + + b.Property("Runtime") + .HasColumnType("TEXT"); + + b.Property("SyncStatus") + .HasColumnType("INTEGER"); + + b.Property("Title") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("TEXT"); + + b.Property("Website") + .HasColumnType("TEXT"); + + b.Property("Writer") + .HasColumnType("TEXT"); + + b.Property("Year") + .HasColumnType("TEXT"); + + b.Property("imdbID") + .HasColumnType("TEXT"); + + b.Property("imdbRating") + .HasColumnType("TEXT"); + + b.Property("imdbVotes") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Movies"); + }); + + modelBuilder.Entity("PlixP.Models.Rating", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CreationDate") + .HasColumnType("TEXT"); + + b.Property("IsRemoved") + .HasColumnType("INTEGER"); + + b.Property("MovieId") + .HasColumnType("INTEGER"); + + b.Property("Source") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.ToTable("Ratings"); + }); + + modelBuilder.Entity("PlixP.Models.CategoryMovie", b => + { + b.HasOne("PlixP.Models.Category", "Category") + .WithMany("Movies") + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("PlixP.Models.Movie", "Movie") + .WithMany("Category") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Category"); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("PlixP.Models.Rating", b => + { + b.HasOne("PlixP.Models.Movie", "Movie") + .WithMany("Ratings") + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Movie"); + }); + + modelBuilder.Entity("PlixP.Models.Category", b => + { + b.Navigation("Movies"); + }); + + modelBuilder.Entity("PlixP.Models.Movie", b => + { + b.Navigation("Category"); + + b.Navigation("Ratings"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/PlixP/Models/BaseDto.cs b/PlixP/Models/BaseDto.cs new file mode 100644 index 0000000..16914ea --- /dev/null +++ b/PlixP/Models/BaseDto.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using AutoMapper; + +namespace PlixP.Models +{ + /// + /// Base Dto Class initial map config between entity and dto + /// + /// Type of Dto Class + /// Type of Entity Class + public abstract class BaseDto : IMapperConf + where TDto : class, new() + where TEntity : new() + { + + public TEntity ToEntity() + { + return Mapper.Map(CastToDerivedClass(this)); + } + + public TEntity ToEntity(TEntity entity) + { + return Mapper.Map(CastToDerivedClass(this), entity); + } + + public static TDto FromEntity(TEntity model) + { + return Mapper.Map(model); + } + + protected TDto CastToDerivedClass(BaseDto baseInstance) + { + return Mapper.Map(baseInstance); + } + + public virtual void MapperConfig(Profile profile) + { + var mappingExpression = profile.CreateMap(); + + var dtoType = typeof(TDto); + var entityType = typeof(TEntity); + //Ignore any property of source (like Post.Author) that dose not contains in destination + foreach (var property in entityType.GetProperties()) + { + if (dtoType.GetProperty(property.Name) == null) + mappingExpression.ForMember(property.Name, opt => opt.Ignore()); + } + + CustomMappings(mappingExpression.ReverseMap()); + } + + public virtual void CustomMappings(IMappingExpression mapping) + { + } + } +} diff --git a/PlixP/Models/Category.cs b/PlixP/Models/Category.cs new file mode 100644 index 0000000..8816f0b --- /dev/null +++ b/PlixP/Models/Category.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PlixP.Models +{ + [Serializable] + public class Category : Entity + { + public string Name { get; set; } + public virtual ICollection Movies { get; set; } + } +} diff --git a/PlixP/Models/CategoryMovie.cs b/PlixP/Models/CategoryMovie.cs new file mode 100644 index 0000000..0578b7a --- /dev/null +++ b/PlixP/Models/CategoryMovie.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PlixP.Models +{ + [Serializable] + public class CategoryMovie : Entity + { + public int CategoryId { get; set; } + public int MovieId { get; set; } + public Category Category { get; set; } + public Movie Movie { get; set; } + [NotMapped] + public bool IsEditable { get; set; } = false; + + } +} diff --git a/PlixP/Models/CustomMappingProfile.cs b/PlixP/Models/CustomMappingProfile.cs new file mode 100644 index 0000000..903fafe --- /dev/null +++ b/PlixP/Models/CustomMappingProfile.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using AutoMapper; + +namespace PlixP.Models +{ + public class CustomMappingProfile : Profile + { + public CustomMappingProfile(IEnumerable mapperConfs) + { + foreach (var mapperConf in mapperConfs) + mapperConf.MapperConfig(this); + } + } +} diff --git a/PlixP/Models/Entity.cs b/PlixP/Models/Entity.cs new file mode 100644 index 0000000..9ecb0cd --- /dev/null +++ b/PlixP/Models/Entity.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PlixP.Models +{ + [Serializable] + public class Entity + { + [Key] + public int Id { get; set; } + public bool IsRemoved { get; set; } + public DateTime CreationDate { get; set; } + } +} diff --git a/PlixP/Models/IMapperConf.cs b/PlixP/Models/IMapperConf.cs new file mode 100644 index 0000000..36664e8 --- /dev/null +++ b/PlixP/Models/IMapperConf.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using AutoMapper; + +namespace PlixP.Models +{ + public interface IMapperConf + { + void MapperConfig(Profile profile); + } +} diff --git a/PlixP/Models/Movie.cs b/PlixP/Models/Movie.cs new file mode 100644 index 0000000..6b3a6c2 --- /dev/null +++ b/PlixP/Models/Movie.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel.DataAnnotations.Schema; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Binary; +using System.Text; +using System.Threading.Tasks; + +namespace PlixP.Models +{ + public enum SyncStatus + { + Synced, + NotSynced + } + [Serializable] + public class Movie : Entity, ICloneable + { + public string FullName + { + get + { + if (Location != null && FileName != null) + return Path.Combine(Location, FileName); + else + return null; + + } + } + public string FileName { get; set; } + public string Location { get; set; } + public string Quality { get; set; } + public string Title { get; set; } + public string Year { get; set; } + + public int IntYear + { + get + { + int year; + if (string.IsNullOrEmpty(Year)) + return 0; + if (int.TryParse(Year, out year)) + return year; + else + return 0; + } + } + + public string Rated { get; set; } + public string Released { get; set; } + public string Runtime { get; set; } + public string Genre { get; set; } + public string Director { get; set; } + public string Writer { get; set; } + public string Actors { get; set; } + public string Plot { get; set; } + public string Language { get; set; } + public string Country { get; set; } + public string Awards { get; set; } + public string Poster { get; set; } + public string Metascore { get; set; } + public string imdbRating { get; set; } + public string imdbVotes { get; set; } + public string imdbID { get; set; } + public string Type { get; set; } + public string DVD { get; set; } + public string BoxOffice { get; set; } + public string Production { get; set; } + public string Website { get; set; } + public string Response { get; set; } + + [NotMapped] + public bool IsDubbed + { + get + { + if (FullName != null && FullName.ToLower().Contains("dubbed")) + return true; + else + return false; + } + } + + public bool IsSeen { get; set; } + + [NotMapped] + public Color SeenStatusColor + { + get + { + if (IsSeen) + return Color.LimeGreen; + else + return Color.OrangeRed; + } + } + + public SyncStatus SyncStatus { get; set; } + public List Ratings { get; set; } + public ObservableCollection Category { get; set; } + [NotMapped] + public List CategoryNames { get; set; } + + public object Clone() + { + using (MemoryStream ms = new MemoryStream()) + { + BinaryFormatter formatter = new BinaryFormatter(); + formatter.Context = new StreamingContext(StreamingContextStates.Clone); + formatter.Serialize(ms, this); + ms.Position = 0; + return formatter.Deserialize(ms); + } + } + + } +} diff --git a/PlixP/Models/MovieDto.cs b/PlixP/Models/MovieDto.cs new file mode 100644 index 0000000..8dedbcd --- /dev/null +++ b/PlixP/Models/MovieDto.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel.DataAnnotations.Schema; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Binary; +using System.Text; +using System.Threading.Tasks; +using AutoMapper; + +namespace PlixP.Models +{ + public class MovieDto : BaseDto + { + public int Id { get; set; } + public bool IsRemoved { get; set; } + public DateTime CreationDate { get; set; } + public string FullName + { + get; + set; + } + public string FileName { get; set; } + public string Location { get; set; } + public string Quality { get; set; } + public string Title { get; set; } + public string Year { get; set; } + public string Rated { get; set; } + public string Released { get; set; } + public string Runtime { get; set; } + public string Genre { get; set; } + public string Director { get; set; } + public string Writer { get; set; } + public string Actors { get; set; } + public string Plot { get; set; } + public string Language { get; set; } + public string Country { get; set; } + public string Awards { get; set; } + public string Poster { get; set; } + public string Metascore { get; set; } + public string imdbRating { get; set; } + public string imdbVotes { get; set; } + public string imdbID { get; set; } + public string Type { get; set; } + public string DVD { get; set; } + public string BoxOffice { get; set; } + public string Production { get; set; } + public string Website { get; set; } + public string Response { get; set; } + public bool IsDubbed + { + get; + set; + } + + public bool IsSeen { get; set; } + + public Color SeenStatusColor + { + get; + set; + } + + public SyncStatus SyncStatus { get; set; } + public List Ratings { get; set; } + public ObservableCollection Category { get; set; } + public List CategoryNames { get; set; } + public override void CustomMappings(IMappingExpression mapping) + { + base.CustomMappings(mapping); + mapping.ForMember(m => m.CategoryNames + , org => org.MapFrom(m => m.Category.Select(c => c.Category.Name).ToList())); + } + } +} diff --git a/PlixP/Models/MovieListModel.cs b/PlixP/Models/MovieListModel.cs new file mode 100644 index 0000000..711c9ad --- /dev/null +++ b/PlixP/Models/MovieListModel.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +namespace PlixP.Models +{ + public class MovieListModel : INotifyPropertyChanged + { + public ObservableCollection OriginalMovies { get; set; } = new ObservableCollection(); + public ObservableCollection OriginalCategories { get; set; } = new ObservableCollection(); + public Dictionary OriginalGenres { get; set; } = new Dictionary(); + + public List Genres + { + get + { + return OriginalGenres.Select(g => string.Format("{0} | {1}", g.Key, g.Value.ToString())).ToList(); + } + } + + public ObservableCollection UnPagedMovies { get; set; } = new ObservableCollection(); + public ObservableCollection PageMovies { get; set; } = new ObservableCollection(); + + public int MoviesCount { get; set; } + public bool AllChecked { get; set; } + + public event PropertyChangedEventHandler PropertyChanged; + + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/PlixP/Models/PlixContext.cs b/PlixP/Models/PlixContext.cs new file mode 100644 index 0000000..8dbe986 --- /dev/null +++ b/PlixP/Models/PlixContext.cs @@ -0,0 +1,19 @@ +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PlixP.Models +{ + public class PlixContext : DbContext + { + protected override void OnConfiguring(DbContextOptionsBuilder options) + => options.UseSqlite("Data Source=plix.db"); + public DbSet Movies { get; set; } + public DbSet Ratings { get; set; } + public DbSet Categories { get; set; } + public DbSet CategoryMovies { get; set; } + } +} diff --git a/PlixP/Models/Rating.cs b/PlixP/Models/Rating.cs new file mode 100644 index 0000000..3834978 --- /dev/null +++ b/PlixP/Models/Rating.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PlixP.Models +{ + [Serializable] + public class Rating : Entity + { + public string Source { get; set; } + public string Value { get; set; } + public int MovieId { get; set; } + public Movie Movie { get; set; } + } +} diff --git a/PlixP/PlixLogo.ico b/PlixP/PlixLogo.ico new file mode 100644 index 0000000..4ffdaf5 Binary files /dev/null and b/PlixP/PlixLogo.ico differ diff --git a/PlixP/PlixP.csproj b/PlixP/PlixP.csproj new file mode 100644 index 0000000..1a187a7 --- /dev/null +++ b/PlixP/PlixP.csproj @@ -0,0 +1,46 @@ + + + WinExe + net5.0-windows + true + PlixP + PlixLogo.ico + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/PlixP/Renders/ImageCacher.cs b/PlixP/Renders/ImageCacher.cs new file mode 100644 index 0000000..14241c9 --- /dev/null +++ b/PlixP/Renders/ImageCacher.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using System.Windows.Media.Imaging; + +namespace PlixP.Renders +{ + public class ImageCacher : Image + { + private string BaseRepos = string.Format("{0}\\PlixMovieCacheFolder\\", Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)); + private List FileInfos; + private bool Sourced = false; + + + public static readonly DependencyProperty ImageSourceProperty = + DependencyProperty.Register(nameof(ImageSource), typeof(string), typeof(ImageCacher), + new PropertyMetadata("", new PropertyChangedCallback(OnSetTextChanged))); + + private static void OnSetTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var imageC = d as ImageCacher; + var source = e.NewValue as string; + if (source != null) + { + var name = source.ToString().Split('/').Last(); + var image = imageC.FileInfos.FirstOrDefault(f => f.Name == name); + if (image != null) + { + imageC.Sourced = true; + var src = new BitmapImage(); + src.BeginInit(); + src.UriSource = new Uri(Path.Combine(image.DirectoryName, image.Name), UriKind.Absolute); + src.CacheOption = BitmapCacheOption.OnLoad; + src.EndInit(); + imageC.Source = src; + } + else + { + try + { + App.Current.Dispatcher.Invoke(() => + { + using (WebClient webClient = new WebClient()) + { + if (source != "N/A") + { + byte[] data = webClient.DownloadData(source.ToString()); + File.WriteAllBytes(Path.Combine(imageC.BaseRepos, name), data); + imageC.Source = new BitmapImage(new Uri(Path.Combine(imageC.BaseRepos, name))); + imageC.Sourced = true; + } + } + }); + } + catch (Exception exception) + { + MessageBox.Show(exception.Message); + } + } + + + } + } + + + public string ImageSource + { + get { return (string)GetValue(ImageSourceProperty); } + set { SetValue(ImageSourceProperty, value); } + } + + public ImageCacher() + { + var directoryInfo = new DirectoryInfo(BaseRepos); + if (!Directory.Exists(BaseRepos)) + { + Directory.CreateDirectory(BaseRepos); + } + FileInfos = directoryInfo.GetFiles().ToList(); + if (FileInfos == null) + FileInfos = new List(); + } + } +} diff --git a/PlixP/Repositories/BaseRepository.cs b/PlixP/Repositories/BaseRepository.cs new file mode 100644 index 0000000..587f32a --- /dev/null +++ b/PlixP/Repositories/BaseRepository.cs @@ -0,0 +1,220 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using PlixP.Extentions; +using PlixP.Models; +using PlixP.Repositories.Contracts; + +namespace PlixP.Repositories +{ + public class BaseRepository : IBaseRepository + where T : Entity + { + protected readonly PlixContext DbContext; + public DbSet Entities { get; } + public virtual IQueryable Table => Entities.Where(e => e.IsRemoved == false); + public virtual IQueryable TableNoTracking => Entities.AsNoTracking().Where(e => e.IsRemoved == false); + + public BaseRepository(PlixContext dbContext) + { + DbContext = dbContext; + Entities = DbContext.Set(); // City => Cities + } + + #region Async Method + public virtual async Task GetByIdAsync(CancellationToken cancellationToken, params object[] ids) + { + + var entity = await Entities.FindAsync(ids, cancellationToken); + DbContext.Entry(entity).State = EntityState.Detached; + return entity; + + } + + public virtual async Task AddAsync(T entity, CancellationToken cancellationToken, bool saveNow = true) + { + Assert.NotNull(entity, nameof(entity)); + entity.CreationDate = DateTime.Now; + await Entities.AddAsync(entity, cancellationToken).ConfigureAwait(false); + if (saveNow) + await DbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + } + + public virtual async Task AddRangeAsync(IEnumerable entities, CancellationToken cancellationToken, bool saveNow = true) + { + Assert.NotNull(entities, nameof(entities)); + await Entities.AddRangeAsync(entities, cancellationToken).ConfigureAwait(false); + if (saveNow) + await DbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + } + + public virtual async Task UpdateAsync(T entity, CancellationToken cancellationToken, bool saveNow = true) + { + Assert.NotNull(entity, nameof(entity)); + DbContext.Entry(entity).State = EntityState.Detached; + await DbContext.SaveChangesAsync(cancellationToken); + //DbContext.Entry(entity).State = EntityState.Modified; + DbContext.Update(entity); + //DbContext.Entry(entity).CurrentValues.SetValues(entity); + if (saveNow) + await DbContext.SaveChangesAsync(cancellationToken); + } + + public virtual async Task UpdateRangeAsync(IEnumerable entities, CancellationToken cancellationToken, bool saveNow = true) + { + Assert.NotNull(entities, nameof(entities)); + Entities.UpdateRange(entities); + if (saveNow) + await DbContext.SaveChangesAsync(cancellationToken); + } + + public virtual async Task DeleteAsync(T entity, CancellationToken cancellationToken, bool saveNow = true) + { + Assert.NotNull(entity, nameof(entity)); + entity.IsRemoved = true; + Entities.Update(entity); + if (saveNow) + await DbContext.SaveChangesAsync(cancellationToken); + } + + public async Task HardDeleteAsync(T entity, CancellationToken cancellationToken, bool saveNow = true) + { + Assert.NotNull(entity, nameof(entity)); + Entities.Remove(entity); + if (saveNow) + await DbContext.SaveChangesAsync(cancellationToken); + } + + public virtual async Task DeleteRangeAsync(IEnumerable entities, CancellationToken cancellationToken, bool saveNow = true) + { + Assert.NotNull(entities, nameof(entities)); + foreach (var apiEntity in entities) + { + apiEntity.IsRemoved = true; + Entities.Update(apiEntity); + } + if (saveNow) + await DbContext.SaveChangesAsync(cancellationToken); + } + #endregion + + #region Sync Methods + public virtual T GetById(params object[] ids) + { + var ent = Entities.Find(ids); + Detach(ent); + return ent; + } + + public virtual void Add(T entity, bool saveNow = true) + { + Assert.NotNull(entity, nameof(entity)); + Entities.Add(entity); + if (saveNow) + DbContext.SaveChanges(); + } + + public virtual void AddRange(IEnumerable entities, bool saveNow = true) + { + Assert.NotNull(entities, nameof(entities)); + Entities.AddRange(entities); + if (saveNow) + DbContext.SaveChanges(); + } + + public virtual void Update(T entity, bool saveNow = true) + { + Assert.NotNull(entity, nameof(entity)); + Detach(entity); + Entities.Update(entity); + DbContext.SaveChanges(); + } + + public virtual void UpdateRange(IEnumerable entities, bool saveNow = true) + { + Assert.NotNull(entities, nameof(entities)); + Entities.UpdateRange(entities); + if (saveNow) + DbContext.SaveChanges(); + } + + public virtual void Delete(T entity, bool saveNow = true) + { + Assert.NotNull(entity, nameof(entity)); + Entities.Remove(entity); + if (saveNow) + DbContext.SaveChanges(); + } + + public virtual void DeleteRange(IEnumerable entities, bool saveNow = true) + { + Assert.NotNull(entities, nameof(entities)); + Entities.RemoveRange(entities); + if (saveNow) + DbContext.SaveChanges(); + } + #endregion + + #region Attach & Detach + public virtual void Detach(T entity) + { + Assert.NotNull(entity, nameof(entity)); + var entry = DbContext.Entry(entity); + if (entry != null) + entry.State = EntityState.Detached; + } + + public virtual void Attach(T entity) + { + Assert.NotNull(entity, nameof(entity)); + if (DbContext.Entry(entity).State == EntityState.Detached) + Entities.Attach(entity); + } + #endregion + + #region Explicit Loading + public virtual async Task LoadCollectionAsync(T entity, Expression>> collectionProperty, CancellationToken cancellationToken) + where TProperty : class + { + Attach(entity); + + var collection = DbContext.Entry(entity).Collection(collectionProperty); + if (!collection.IsLoaded) + await collection.LoadAsync(cancellationToken).ConfigureAwait(false); + } + + public virtual void LoadCollection(T entity, Expression>> collectionProperty) + where TProperty : class + { + Attach(entity); + var collection = DbContext.Entry(entity).Collection(collectionProperty); + if (!collection.IsLoaded) + collection.Load(); + } + + public virtual async Task LoadReferenceAsync(T entity, Expression> referenceProperty, CancellationToken cancellationToken) + where TProperty : class + { + Attach(entity); + var reference = DbContext.Entry(entity).Reference(referenceProperty); + if (!reference.IsLoaded) + await reference.LoadAsync(cancellationToken).ConfigureAwait(false); + } + + public virtual void LoadReference(T entity, Expression> referenceProperty) + where TProperty : class + { + Attach(entity); + var reference = DbContext.Entry(entity).Reference(referenceProperty); + if (!reference.IsLoaded) + reference.Load(); + } + #endregion + + } +} diff --git a/PlixP/Repositories/Contracts/IBaseRepository.cs b/PlixP/Repositories/Contracts/IBaseRepository.cs new file mode 100644 index 0000000..5d0891f --- /dev/null +++ b/PlixP/Repositories/Contracts/IBaseRepository.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using PlixP.Models; + +namespace PlixP.Repositories.Contracts +{ + public interface IBaseRepository where T : Entity + { + DbSet Entities { get; } + IQueryable Table { get; } + IQueryable TableNoTracking { get; } + + void Add(T entity, bool saveNow = true); + Task AddAsync(T entity, CancellationToken cancellationToken, bool saveNow = true); + void AddRange(IEnumerable entities, bool saveNow = true); + Task AddRangeAsync(IEnumerable entities, CancellationToken cancellationToken, bool saveNow = true); + void Attach(T entity); + void Delete(T entity, bool saveNow = true); + Task DeleteAsync(T entity, CancellationToken cancellationToken, bool saveNow = true); + Task HardDeleteAsync(T entity, CancellationToken cancellationToken, bool saveNow = true); + void DeleteRange(IEnumerable entities, bool saveNow = true); + Task DeleteRangeAsync(IEnumerable entities, CancellationToken cancellationToken, bool saveNow = true); + void Detach(T entity); + T GetById(params object[] ids); + Task GetByIdAsync(CancellationToken cancellationToken, params object[] ids); + void LoadCollection(T entity, Expression>> collectionProperty) where TProperty : class; + Task LoadCollectionAsync(T entity, Expression>> collectionProperty, CancellationToken cancellationToken) where TProperty : class; + void LoadReference(T entity, Expression> referenceProperty) where TProperty : class; + Task LoadReferenceAsync(T entity, Expression> referenceProperty, CancellationToken cancellationToken) where TProperty : class; + void Update(T entity, bool saveNow = true); + Task UpdateAsync(T entity, CancellationToken cancellationToken, bool saveNow = true); + void UpdateRange(IEnumerable entities, bool saveNow = true); + Task UpdateRangeAsync(IEnumerable entities, CancellationToken cancellationToken, bool saveNow = true); + } +} diff --git a/PlixP/Repositories/Contracts/IRepositoryWrapper.cs b/PlixP/Repositories/Contracts/IRepositoryWrapper.cs new file mode 100644 index 0000000..4636c28 --- /dev/null +++ b/PlixP/Repositories/Contracts/IRepositoryWrapper.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using PlixP.Models; + +namespace PlixP.Repositories.Contracts +{ + public interface IRepositoryWrapper + { + IBaseRepository SetRepository() where T : Entity; + } +} diff --git a/PlixP/Repositories/RepositoryWrapper.cs b/PlixP/Repositories/RepositoryWrapper.cs new file mode 100644 index 0000000..ed5555c --- /dev/null +++ b/PlixP/Repositories/RepositoryWrapper.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using PlixP.Models; +using PlixP.Repositories.Contracts; + +namespace PlixP.Repositories +{ + public class RepositoryWrapper : IRepositoryWrapper + { + private readonly PlixContext _context; + public RepositoryWrapper(PlixContext context) + { + _context = context; + } + public IBaseRepository SetRepository() where T : Entity + { + IBaseRepository repository = new BaseRepository(_context); + return repository; + } + } +} diff --git a/PlixP/Resources/PlixLogo.ico b/PlixP/Resources/PlixLogo.ico new file mode 100644 index 0000000..4ffdaf5 Binary files /dev/null and b/PlixP/Resources/PlixLogo.ico differ diff --git a/PlixP/Resources/PlixLogo.png b/PlixP/Resources/PlixLogo.png new file mode 100644 index 0000000..242403f Binary files /dev/null and b/PlixP/Resources/PlixLogo.png differ diff --git a/PlixP/Resources/SPLASH.png b/PlixP/Resources/SPLASH.png new file mode 100644 index 0000000..28f9e33 Binary files /dev/null and b/PlixP/Resources/SPLASH.png differ diff --git a/PlixP/Services/CatcherService.cs b/PlixP/Services/CatcherService.cs new file mode 100644 index 0000000..bb3f900 --- /dev/null +++ b/PlixP/Services/CatcherService.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Plugin.Settings; + +namespace PlixP.Services +{ + public static class CatcherKeys + { + public static string MovieList { get; } = "Movies"; + } + public class CatcherService + { + public void Set(object item, string key = null) + { + if (string.IsNullOrEmpty(key)) + { + key = item.GetType().Name; + } + + var json = JsonConvert.SerializeObject(item,Formatting.None, new JsonSerializerSettings() + { + ReferenceLoopHandling = ReferenceLoopHandling.Ignore + }); + Plugin.Settings.CrossSettings.Current.AddOrUpdateValue(key, json); + } + + public T Get(string key = null) + { + if (string.IsNullOrEmpty(key)) + key = typeof(T).Name; + var json = CrossSettings.Current.GetValueOrDefault(key, string.Empty); + if (string.IsNullOrEmpty(json)) + return Activator.CreateInstance(); + return JsonConvert.DeserializeObject(json); + + } + + public void Delete(string key = null) + { + if (string.IsNullOrEmpty(key)) + key = typeof(T).Name; + CrossSettings.Current.Remove(key); + } + } +} diff --git a/PlixP/Services/Contracts/IServiceWrapper.cs b/PlixP/Services/Contracts/IServiceWrapper.cs new file mode 100644 index 0000000..c155ee2 --- /dev/null +++ b/PlixP/Services/Contracts/IServiceWrapper.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PlixP.Services.Contracts +{ + public interface IServiceWrapper + { + LocalServices LocalServices { get; } + MovieServices MovieServices { get; } + CatcherService CatcherService { get; } + } +} diff --git a/PlixP/Services/LocalServices.cs b/PlixP/Services/LocalServices.cs new file mode 100644 index 0000000..a43732f --- /dev/null +++ b/PlixP/Services/LocalServices.cs @@ -0,0 +1,216 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; + +namespace PlixP.Services +{ + + public class LocalServices + { + private string[] extentions = { ".mp4", ".mkv", ".avi" }; + private List _baseDires = new List(); + private List paths = new List(); + public LocalServices() + { + + var pathsJson = Plugin.Settings.CrossSettings.Current.GetValueOrDefault("Paths", string.Empty); + paths = Newtonsoft.Json.JsonConvert.DeserializeObject>(pathsJson); + if (paths == null) + paths = new List(); + if (paths.Count > 0) + paths.ForEach(p => _baseDires.Add(new DirectoryInfo(p))); + } + + public void ResetFolder() + { + RenameAllFile(GetMovieFiles()); + CreateUnFolders(GetMovieFiles()); + RenameAllFolder(); + } + + public string GetFolderNameByMovieFileName(string movieN) + { + string movieName = string.Empty; + bool yeared = false; + bool qualited = false; + movieN = movieN.Replace('-', '.'); + movieN = movieN.Replace('_', '.'); + foreach (var str in movieN.Split('.')) + { + int year = 0; + if (int.TryParse(str, out year) && (year >= 1920 && year <= 2142)) + { + movieName += "(" + year + ")" + " "; + yeared = true; + } + else if (str.Contains("720") || str.Contains("1080") || str.ToLower().Contains("dvdsrc") || str.ToLower().Contains("hdrip") || str.ToLower().Contains("dvdrip")) + { + movieName += "[" + str + "]"; + qualited = true; + } + else if (!yeared && !qualited) + { + movieName += str + " "; + } + } + + return movieName; + } + private void RenameAllFolder() + { + try + { + if (paths.Count > 0) + { + foreach (var path in paths) + { + DirectoryInfo directoryInfo = new DirectoryInfo(path); + string baseDire = directoryInfo.Name; + var dires = GetDirectoryInfos(directoryInfo); + foreach (var dir in dires) + { + string movieN = string.Empty; + string movieName = string.Empty; + foreach (var fileInfo in dir.GetFiles()) + { + if (fileInfo.Name.Contains(extentions[0]) || fileInfo.Name.Contains(extentions[1]) || fileInfo.Name.Contains(extentions[2])) + movieN = fileInfo.Name; + } + + if (!string.IsNullOrEmpty(movieN)) + { + movieName = GetFolderNameByMovieFileName(movieN); + DirectoryInfo di = new DirectoryInfo(string.Concat(dir.Parent, "\\", movieName)); + if (!di.Exists) + Directory.Move(dir.FullName, string.Concat(dir.Parent, "\\", movieName)); + } + + } + } + } + } + catch (Exception e) + { + MessageBox.Show(e.Message); + } + } + public List GetMovieFiles() + { + if (paths.Count > 0) + { + var allFiles = new List(); + foreach (var path in paths) + { + DirectoryInfo dir = new DirectoryInfo(path); + string baseDire = dir.Name; + var dires = GetDirectoryInfos(dir); + List files = new List(); + foreach (var directoryInfo in dires) + { + files.AddRange(directoryInfo.GetFiles()); + } + allFiles.AddRange(files); + } + return allFiles.Where(f => extentions.Contains(f.Extension)).ToList(); + } + else + return new List(); + } + public void RenameAllFile(List fileInfos) + { + fileInfos.ForEach(m => + { + string newName = ""; + foreach (var str in m.DirectoryName.Split("\\")) + { + newName += str + "\\"; + } + var name = m.Name.Replace('_', '.'); + name = name.Replace(' ', '.'); + newName += name; + File.Move(m.FullName, newName); + }); + } + public void CreateUnFolders(List movies) + { + foreach (var baseDire in _baseDires) + { + + movies.Where(m => m.DirectoryName.Split('\\').Last() == baseDire.Name).ToList().ForEach(m => + { + try + { + string movieName = ""; + bool yeared = false; + bool qualited = false; + foreach (var str in m.Name.Split('.')) + { + int year = 0; + if (int.TryParse(str, out year) && (year >= 1920 && year <= 2142)) + { + movieName += "(" + year + ")" + " "; + yeared = true; + } + else if (str.Contains("720") || str.Contains("1080") || str.ToLower().Contains("dvdsrc") || str.ToLower().Contains("hdrip") || str.ToLower().Contains("dvdrip")) + { + movieName += "[" + str + "]"; + qualited = true; + } + else if (!yeared && !qualited && !extentions.Contains(str)) + { + movieName += str + " "; + + } + } + + var newDire = Path.Combine(m.DirectoryName, movieName).Trim(); + Directory.CreateDirectory(newDire); + var orgFile = m.FullName; + var desFile = Path.Combine(newDire, m.Name); + File.Move(orgFile, desFile); + + } + catch (Exception e) + { + Console.WriteLine(e); + MessageBox.Show(e.Message); + } + }); + } + } + public List GetDirectoryInfos(DirectoryInfo baseDirectoryInfo) + { + try + { + List directoryInfos = new List(); + var dires = baseDirectoryInfo.GetDirectories(); + if (dires.Length > 0) + { + foreach (var directory in dires) + { + directoryInfos.AddRange(GetDirectoryInfos(directory)); + } + } + directoryInfos.Add(baseDirectoryInfo); + return directoryInfos; + } + catch (Exception e) + { + Console.WriteLine(e); + return new List(); + } + } + public void RenameFile(FileInfo oldFile, FileInfo newFile) + { + File.Move(oldFile.FullName, newFile.FullName); + } + public void RenameDirection(DirectoryInfo oldDirectory, DirectoryInfo newDirectory) + { + Directory.Move(oldDirectory.FullName, newDirectory.FullName); + } + } +} diff --git a/PlixP/Services/MovieServices.cs b/PlixP/Services/MovieServices.cs new file mode 100644 index 0000000..68a268b --- /dev/null +++ b/PlixP/Services/MovieServices.cs @@ -0,0 +1,389 @@ +using RestSharp; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows; +using AutoMapper.QueryableExtensions; +using HandyControl.Controls; +using MaterialDesignThemes.Wpf; +using Microsoft.EntityFrameworkCore; +using PlixP.Models; +using PlixP.Repositories; +using PlixP.ViewModels; +using PlixP.Views.Dialogs; +using MessageBox = HandyControl.Controls.MessageBox; +using Timer = System.Timers.Timer; + +namespace PlixP.Services +{ + public class MovieServiceRealTime + { + private static MovieServiceRealTime _instance; + public static MovieServiceRealTime Instance + { + get + { + if (_instance == null) + _instance = new MovieServiceRealTime(); + return _instance; + } + } + + private LocalServices _localServices; + private MovieServices _movieServices; + private bool IsWorking = false; + private List _currentFiles = new List(); + private Timer timer; + public List CurreMovies + { + get { return _movieServices.GetMovies().Result; } + } + + public MovieServiceRealTime() + { + timer = new Timer(6000); + _localServices = new LocalServices(); + _movieServices = new MovieServices(); + timer.Elapsed += Timer_Elapsed; + } + + public void StartReal() => timer.Start(); + + private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) + { + CheckAll(); + } + + private async Task CheckAll() + { + try + { + if (IsWorking) + return; + IsWorking = true; + _localServices.ResetFolder(); + var movieFolders = _localServices.GetMovieFiles(); + foreach (var f in movieFolders) + { + if (!CurreMovies.Any(m => m.Location == f.DirectoryName)) + { + await _movieServices.AddNewMovieToDb(f); + Application.Current.Dispatcher.Invoke(() => + { + Growl.SuccessGlobal("Movie Added : " + f.Name); + }); + } + } + + foreach (var m in CurreMovies.Where(m => m.SyncStatus == SyncStatus.NotSynced)) + { + var creationDate = m.CreationDate; + var movie = await _movieServices.GetMovieAsync(m.Title); + if (movie != null) + { + movie.CreationDate = creationDate; + movie.Id = m.Id; + movie.Location = m.Location; + movie.FileName = m.FileName; + movie.SyncStatus = SyncStatus.Synced; + await _movieServices.EditMovie(movie, SyncStatus.Synced); + Application.Current.Dispatcher.Invoke(() => + { + Growl.WarningGlobal("Movie Synced : " + movie.FullName); + }); + } + } + + + + IsWorking = false; + } + catch (Exception e) + { + MessageBox.Show(e.Message); + } + } + } + public class MovieServices + { + private readonly RepositoryWrapper _repositoryWrapper; + private readonly LocalServices _localServices; + private readonly CatcherService _catcherService; + + public MovieServices() + { + _repositoryWrapper = new RepositoryWrapper(new PlixContext()); + _localServices = new LocalServices(); + _catcherService = new CatcherService(); + } + + + public async Task> GetMovies() + { + try + { + List Movies = new List(); + Movies = _catcherService.Get>(CatcherKeys.MovieList); + if (Movies != null) + if (Movies.Count != 0) + return Movies; + var dtos = _repositoryWrapper.SetRepository() + .TableNoTracking + .ProjectTo() + .ToList(); + dtos.ForEach(m => Movies.Add(m.ToEntity())); + foreach (var movie in Movies) + { + foreach (var categoryMovie in movie.Category) + { + categoryMovie.Category = await _repositoryWrapper.SetRepository() + .GetByIdAsync(CancellationToken.None, categoryMovie.CategoryId); + } + } + _catcherService.Set(Movies, CatcherKeys.MovieList); + return Movies; + } + catch (Exception e) + { + throw e; + } + } + + public async Task AddNewMovieToDb(FileInfo f) + { + var cMovie = new Movie(); + var mName = f.Directory.Name.Split("(")[0]; + cMovie = await GetMovieAsync(mName); + string q = " "; + if (f.Name.Contains("720")) + q = "720P"; + else if (f.Name.Contains("1080")) + q = "1080P"; + if (cMovie != null) + { + cMovie.FileName = f.Name; + cMovie.Location = f.DirectoryName; + cMovie.Quality = q; + cMovie.SyncStatus = SyncStatus.Synced; + cMovie.CreationDate = DateTime.Now; + await _repositoryWrapper.SetRepository().AddAsync(cMovie, CancellationToken.None); + await _repositoryWrapper.SetRepository().AddAsync(new CategoryMovie + { + MovieId = cMovie.Id, + CategoryId = 1 + }, CancellationToken.None); + + } + else + { + cMovie = new Movie(); + cMovie.Location = f.DirectoryName; + cMovie.FileName = f.Name; + cMovie.Quality = q; + cMovie.Title = mName; + cMovie.CreationDate = DateTime.Now; + cMovie.SyncStatus = SyncStatus.NotSynced; + + + await _repositoryWrapper.SetRepository().AddAsync(cMovie, CancellationToken.None); + await _repositoryWrapper.SetRepository().AddAsync(new CategoryMovie + { + MovieId = cMovie.Id, + CategoryId = 1 + }, CancellationToken.None); + } + _catcherService.Delete>(CatcherKeys.MovieList); + return cMovie; + } + public async Task GetMovieAsync(string movieName) + { + RestClient client = new RestClient(); + RestRequest request = new RestRequest("http://omdbapi.com", Method.GET); + request.AddQueryParameter("t", movieName); + request.AddQueryParameter("apikey", "a915dcc9"); + var res = await client.ExecuteGetAsync(request); + if (res.StatusCode == HttpStatusCode.OK) + { + if (res.Data.Response != "False") + return res.Data; + else + { + /*Application.Current.Dispatcher.Invoke(() => + { + Growl.ErrorGlobal($"Cant Sync Movie {movieName} , Please Edit Movie Detail"); + });*/ + return null; + } + } + else + { + /*Application.Current.Dispatcher.Invoke(() => + { + Growl.ErrorGlobal($"Cant Sync Movie {movieName} , Please Edit Movie Detail"); + });*/ + return null; + } + } + public async Task EditMovie(Movie newMovie, SyncStatus syncStatus = SyncStatus.NotSynced) + { + try + { + var oldMovie = await _repositoryWrapper.SetRepository() + .GetByIdAsync(CancellationToken.None, newMovie.Id); + string newMovieFileName = string.Empty; + + foreach (var str in newMovie.Title.Split(" ")) + { + if (string.IsNullOrWhiteSpace(str)) + continue; + newMovieFileName += str + "."; + } + if (!string.IsNullOrWhiteSpace(newMovie.Year)) + newMovieFileName += newMovie.Year + "."; + else + { + foreach (var str in oldMovie.FileName.Split(".")) + { + + int year = 0; + if (int.TryParse(str, out year) && (year >= 1920 && year <= 2142)) + { + newMovieFileName += year + "."; + } + } + } + if (!string.IsNullOrWhiteSpace(newMovie.Quality)) + newMovieFileName += newMovie.Quality + "."; + else + { + bool qulited = false; + foreach (var str in oldMovie.FileName.Split(".")) + { + if (str.Contains("720") || str.Contains("1080") || str.ToLower().Contains("dvdsrc") || str.ToLower().Contains("hdrip") || str.ToLower().Contains("dvdrip")) + { + newMovieFileName += str + "."; + newMovie.Quality = str; + qulited = true; + } + } + if (!qulited) + { + newMovieFileName += "720" + "."; + newMovie.Quality = "720"; + } + } + + newMovieFileName += oldMovie.FileName.Split(".").Last(); + newMovie.FileName = newMovieFileName; + newMovie.FileName = newMovie.FileName.Replace(":", ""); + newMovie.FileName = newMovie.FileName.Replace(@"\", ""); + newMovie.FileName = newMovie.FileName.Replace("?", ""); + newMovie.FileName = newMovie.FileName.Replace("*", ""); + newMovie.FileName = newMovie.FileName.Replace("/", ""); + newMovie.FileName = newMovie.FileName.Replace("<", ""); + newMovie.FileName = newMovie.FileName.Replace(">", ""); + newMovie.FileName = newMovie.FileName.Replace("|", ""); + + var folderName = _localServices.GetFolderNameByMovieFileName(newMovie.FileName); + var location = Path.Combine(newMovie.Location.Split("\\").Take(newMovie.Location.Split("\\").Length - 1) + .ToArray()); + newMovie.Location = $"{Path.Combine(location, folderName)}"; + newMovie.SyncStatus = syncStatus; + newMovie.Category = null; + await _repositoryWrapper.SetRepository().UpdateAsync(newMovie, CancellationToken.None); + if (oldMovie.Location != newMovie.Location) + _localServices.RenameDirection(new DirectoryInfo(oldMovie.Location), new DirectoryInfo(newMovie.Location)); + oldMovie.Location = Path.Combine(location, folderName); + _localServices.RenameFile(new FileInfo(oldMovie.FullName), new FileInfo(newMovie.FullName)); + _catcherService.Delete>(CatcherKeys.MovieList); + MessageBox.Show($"Movie {newMovie.Title} Was Edited !"); + } + catch (Exception e) + { + MessageBox.Show(e.Message); + } + } + public async Task> GetGenres() + { + List Movies = await GetMovies(); + var originalGenres = Movies.Select(m => m.Genre).ToList(); + Dictionary genres = new Dictionary(); + foreach (var originalGenre in originalGenres) + { + if (string.IsNullOrEmpty(originalGenre)) + continue; + var split = originalGenre.Split(','); + foreach (var str in split) + { + var trim = str.Trim(); + if (genres.ContainsKey(trim)) + genres[trim]++; + else + genres.Add(trim, 1); + } + + } + return genres; + } + public async Task> GetGenresModel() + { + List Movies = await GetMovies(); + var originalGenres = Movies.Select(m => m.Genre).ToList(); + List genres = new List(); + foreach (var originalGenre in originalGenres) + { + if (string.IsNullOrEmpty(originalGenre)) + continue; + var split = originalGenre.Split(','); + foreach (var str in split) + { + var trim = str.Trim(); + var genre = genres.FirstOrDefault(g=>g.Name==trim); + if (genre!=null) + genre.MovieCount++; + else + { + var item = new GenreItemModel + { + MovieCount = 1, + Name = trim + }; + foreach (var movie in Movies.Where(m=>m.Genre.Contains(trim))) + { + var poster = movie.Poster; + if (!genres.Any(g => g.Image == poster)) + item.Image = poster; + } + + if (item.Image == null) + item.Image = Movies.FirstOrDefault(m => m.Genre.Contains(trim))?.Poster; + genres = genres.OrderByDescending(g => g.MovieCount).ToList(); + genres.Add(item); + } + } + + + } + return genres; + } + public async Task> SelectByGenre(string genre) + { + return (await GetMovies()).Where(m => m.Genre.Contains(genre)) + .ToList(); + } + public async Task DeleteMovie(Movie movie) + { + movie.Category = null; + await _repositoryWrapper.SetRepository() + .DeleteAsync(movie, CancellationToken.None); + _catcherService.Delete>(CatcherKeys.MovieList); + MovieServiceRealTime.Instance.CurreMovies.RemoveAll(m => m.Id == movie.Id); + } + } +} diff --git a/PlixP/Services/ServiceWrapper.cs b/PlixP/Services/ServiceWrapper.cs new file mode 100644 index 0000000..36b291b --- /dev/null +++ b/PlixP/Services/ServiceWrapper.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using PlixP.Services.Contracts; + +namespace PlixP.Services +{ + public class ServiceWrapper : IServiceWrapper + { + private LocalServices _localServices; + + public LocalServices LocalServices + { + get + { + if (_localServices == null) + _localServices = new LocalServices(); + return _localServices; + } + } + + private MovieServices _movieServices; + + public MovieServices MovieServices + { + get + { + if (_movieServices == null) + _movieServices = new MovieServices(); + return _movieServices; + } + } + + private CatcherService _catcherService; + + public CatcherService CatcherService + { + get + { + if (_catcherService == null) + _catcherService = new CatcherService(); + return _catcherService; + } + } + } +} diff --git a/PlixP/ViewModels/AddCategoryDialogViewModel.cs b/PlixP/ViewModels/AddCategoryDialogViewModel.cs new file mode 100644 index 0000000..c791c03 --- /dev/null +++ b/PlixP/ViewModels/AddCategoryDialogViewModel.cs @@ -0,0 +1,16 @@ +using Prism.Commands; +using Prism.Mvvm; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace PlixP.ViewModels +{ + public class AddCategoryDialogViewModel : BindableBase + { + public AddCategoryDialogViewModel() + { + + } + } +} diff --git a/PlixP/ViewModels/GenresDialogViewModel.cs b/PlixP/ViewModels/GenresDialogViewModel.cs new file mode 100644 index 0000000..62bb55b --- /dev/null +++ b/PlixP/ViewModels/GenresDialogViewModel.cs @@ -0,0 +1,45 @@ +using Prism.Commands; +using Prism.Mvvm; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Input; +using PlixP.Repositories.Contracts; +using PlixP.Services.Contracts; + +namespace PlixP.ViewModels +{ + public class GenreItemModel + { + public string Name { get; set; } + public int Id { get; set; } + public string Image { get; set; } + public int MovieCount { get; set; } + } + public class GenresDialogViewModel : BindableBase + { + private readonly IServiceWrapper _serviceWrapper; + private readonly IRepositoryWrapper _repositoryWrapper; + public event EventHandler GenreSelected; + public ICommand GenreSelectCommand { get; set; } + public ObservableCollection Genres { get; set; } = new ObservableCollection(); + public GenresDialogViewModel(IServiceWrapper serviceWrapper,IRepositoryWrapper repositoryWrapper) + { + _serviceWrapper = serviceWrapper; + _repositoryWrapper = repositoryWrapper; + GenreSelectCommand = new DelegateCommand(model => + { + GenreSelected?.Invoke(model, model.Name); + }); + Initialize(); + } + + private async Task Initialize() + { + + (await _serviceWrapper.MovieServices.GetGenresModel()).ForEach(g=>Genres.Add(g)); + } + } +} diff --git a/PlixP/ViewModels/MainWindowViewModel.cs b/PlixP/ViewModels/MainWindowViewModel.cs new file mode 100644 index 0000000..e244b64 --- /dev/null +++ b/PlixP/ViewModels/MainWindowViewModel.cs @@ -0,0 +1,45 @@ +using System.Collections.ObjectModel; +using System.Threading.Tasks; +using PlixP.Services.Contracts; +using PlixP.Views; +using Prism.Mvvm; +using Prism.Regions; + +namespace PlixP.ViewModels +{ + public class MainWindowViewModel : BindableBase + { + private string _title = "Prism Application"; + public string Title + { + get { return _title; } + set { SetProperty(ref _title, value); } + } + + + private int _count; + public int Count + { + get { return _count; } + set { SetProperty(ref _count, value); } + } + + private string _background; + + public string Background + { + get { return _background; } + set { SetProperty(ref _background, value); } + } + + + public MainWindowViewModel(IServiceWrapper serviceWrapper , IRegionManager regionManager) + { + var bg = Plugin.Settings.CrossSettings.Current.GetValueOrDefault("Background", string.Empty); + if (string.IsNullOrEmpty(bg)) + Background = "https://www.jakpost.travel/wimages/large/166-1664172_lord-of-the-rings-background.jpg"; + else + Background = bg; + } + } +} diff --git a/PlixP/ViewModels/MasterDetailViewModel.cs b/PlixP/ViewModels/MasterDetailViewModel.cs new file mode 100644 index 0000000..bfa004d --- /dev/null +++ b/PlixP/ViewModels/MasterDetailViewModel.cs @@ -0,0 +1,107 @@ +using Prism.Commands; +using Prism.Mvvm; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Timers; +using System.Windows; +using MaterialDesignThemes.Wpf; +using Microsoft.EntityFrameworkCore; +using Microsoft.Win32; +using Microsoft.Xaml.Behaviors.Core; +using PlixP.Models; +using PlixP.Repositories.Contracts; +using PlixP.Services.Contracts; +using PlixP.Views; +using PlixP.Views.Dialogs; + +namespace PlixP.ViewModels +{ + public class MasterDetailViewModel : BindableBase + { + public ObservableCollection Paths { get; set; } = new ObservableCollection(); + public DelegateCommand PathCommand { get; set; } + public DelegateCommand AddCategoryCommand { get; set; } + public DelegateCommand ResetFolderCommand { get; set; } + public DelegateCommand ChangeBackGroundCommand { get; set; } + public DelegateCommand RemovePathCommand { get; set; } + public DelegateCommand ResetDbCommand { get; set; } + public MasterDetailViewModel(IRepositoryWrapper repositoryWrapper, IServiceWrapper serviceWrapper) + { + var pathsJson = Plugin.Settings.CrossSettings.Current.GetValueOrDefault("Paths", string.Empty); + if (!string.IsNullOrEmpty(pathsJson)) + { + var paths = Newtonsoft.Json.JsonConvert.DeserializeObject>(pathsJson); + if (paths != null) + paths.ForEach(p => Paths.Add(p)); + } + PathCommand = new DelegateCommand((str) => + { + if (!string.IsNullOrEmpty(str)) + { + Paths.Add(str); + var pathJson = Newtonsoft.Json.JsonConvert.SerializeObject(Paths.ToList()); + Plugin.Settings.CrossSettings.Current.AddOrUpdateValue("Paths", pathJson); + MessageBox.Show("Seccuss"); + //serviceWrapper.LocalServices.ResetFolder(); + } + }); + ResetDbCommand = new DelegateCommand(() => + { + QuestionDialog dialog = new QuestionDialog("ایا از ریست کردن دیتابیس مطمئن هستید ؟"); + dialog.Submited += (async (ss, ee) => + { + var context = new PlixContext(); + await context.Database.EnsureDeletedAsync(); + await context.Database.MigrateAsync(); + }); + DialogHost.OpenDialogCommand.Execute(dialog,null); + }); + RemovePathCommand = new DelegateCommand(str => + { + Paths.Remove(str); + var pathJson = Newtonsoft.Json.JsonConvert.SerializeObject(Paths.ToList()); + Plugin.Settings.CrossSettings.Current.AddOrUpdateValue("Paths", pathJson); + MessageBox.Show("Seccuss"); + }); + AddCategoryCommand = new DelegateCommand(async(cat) => + { + try + { + await repositoryWrapper.SetRepository().AddAsync(new Category + { + Name = cat + },CancellationToken.None); + MessageBox.Show("Seccuss"); + } + catch (Exception e) + { + MessageBox.Show(e.Message); + } + }); + ResetFolderCommand = new DelegateCommand(() => + { + serviceWrapper.LocalServices.ResetFolder(); + }); + ChangeBackGroundCommand = new DelegateCommand( ()=> + { + OpenFileDialog openFileDialog = new OpenFileDialog(); + openFileDialog.Multiselect = false; + openFileDialog.Filter = "Image Files(*.BMP;*.JPG;*.GIF*.PNG)|*.BMP;*.JPG;*.GIF*.PNG|All files (*.*)|*.*"; + openFileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); + if (openFileDialog.ShowDialog() == true) + { + var str = openFileDialog.FileName; + if (!string.IsNullOrEmpty(str)) + { + Plugin.Settings.CrossSettings.Current.AddOrUpdateValue("Background", str); + DialogHost.OpenDialogCommand.Execute(new RestartAppDialog(),null); + } + } + }); + } + } +} diff --git a/PlixP/ViewModels/MovieDialogViewModel.cs b/PlixP/ViewModels/MovieDialogViewModel.cs new file mode 100644 index 0000000..5754d85 --- /dev/null +++ b/PlixP/ViewModels/MovieDialogViewModel.cs @@ -0,0 +1,162 @@ +using Prism.Commands; +using Prism.Mvvm; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Windows.Input; +using HandyControl.Controls; +using HandyControl.Interactivity; +using MaterialDesignThemes.Wpf; +using Microsoft.EntityFrameworkCore; +using PlixP.Models; +using PlixP.Repositories.Contracts; +using PlixP.Services; +using PlixP.Services.Contracts; +using PlixP.Views.Dialogs; +using MessageBox = System.Windows.MessageBox; + +namespace PlixP.ViewModels +{ + public class MovieDialogViewModel : BindableBase + { + private Movie _movie; + public Movie Movie + { + get { return _movie; } + set { SetProperty(ref _movie, value); } + } + + public ICommand DeleteCommand { get; set; } + public ICommand SaveCommand { get; set; } + public ICommand PlayFileCommand { get; set; } + public ICommand OpenFolderCommand { get; set; } + public ICommand AddCategoryCommand { get; set; } + public ICommand RemoveCategoryCommand { get; set; } + private IServiceWrapper ServiceWrapper { get; set; } + private IRepositoryWrapper RepositoryWrapper { get; set; } + public ObservableCollection Categories { get; set; } + public List Properties { get; set; } = new List(); + public MovieDialogViewModel() + { + + } + public MovieDialogViewModel(IServiceWrapper serviceWrapper, IRepositoryWrapper repositoryWrapper, Movie movie) + { + Categories = new ObservableCollection(); + repositoryWrapper.SetRepository().TableNoTracking.ToList().ForEach(c => Categories.Add(c)); + Movie = movie; + ServiceWrapper = serviceWrapper; + RepositoryWrapper = repositoryWrapper; + Properties.Add(new + { + Name = "Director : ", + Value = movie.Director + }); + Properties.Add(new + { + Name = "Writer : ", + Value = movie.Writer + }); + Properties.Add(new + { + Name = "Awarads : ", + Value = movie.Awards + }); + Properties.Add(new + { + Name = "Actors : ", + Value = movie.Actors + }); + Properties.Add(new + { + Name = "Country : ", + Value = movie.Country + }); + DeleteCommand = new DelegateCommand(() => + { + QuestionDialog dialog = new QuestionDialog("ایا از حذف فیلم مطمئن هستید ؟"); + dialog.Submited += (async (ss, ee) => + { + await serviceWrapper.MovieServices.DeleteMovie(movie); + ControlCommands.CloseAll.Execute(null, null); + HandyControl.Controls.MessageBox.Show($"{movie.Title} Has Removed"); + }); + Dialog.Show(dialog); + }); + PlayFileCommand = new DelegateCommand(() => + { + Process process = new Process(); + process.StartInfo.UseShellExecute = true; + process.StartInfo.FileName = @Movie.Location + "\\" + Movie.FullName; + process.Start(); + }); + OpenFolderCommand = new DelegateCommand(() => + { + try + { + Process process = new Process(); + process.StartInfo.UseShellExecute = true; + process.StartInfo.FileName = @Movie.Location; + process.Start(); + } + catch (Exception e) + { + MessageBox.Show(e.Message); + } + }); + AddCategoryCommand = new DelegateCommand(async (category) => + { + if (category.Id != 0) + { + if (Movie.Category.FirstOrDefault(c => c.CategoryId == category.Id) == null) + { + Movie.Category.Add(new CategoryMovie + { + MovieId = Movie.Id, + Category = category, + CategoryId = category.Id + }); + await repositoryWrapper.SetRepository().AddAsync(new CategoryMovie + { + MovieId = Movie.Id, + CategoryId = category.Id + },CancellationToken.None); + } + } + else + { + Movie.Category.Add(new CategoryMovie + { + MovieId = Movie.Id, + Category = category + }); + await repositoryWrapper.SetRepository().AddAsync(new CategoryMovie + { + MovieId = Movie.Id, + Category = category + },CancellationToken.None); + } + + }); + RemoveCategoryCommand = new DelegateCommand(async (cMovie)=> + { + try + { + await RepositoryWrapper.SetRepository().DeleteAsync(cMovie,CancellationToken.None); + Movie.Category.Remove(cMovie); + } + catch (Exception e) + { + MessageBox.Show(e.Message); + } + }); + SaveCommand = new DelegateCommand(async (m) => + { + await ServiceWrapper.MovieServices.EditMovie(m); + }); + } + } +} diff --git a/PlixP/ViewModels/MovieItemViewModel.cs b/PlixP/ViewModels/MovieItemViewModel.cs new file mode 100644 index 0000000..00b3d5b --- /dev/null +++ b/PlixP/ViewModels/MovieItemViewModel.cs @@ -0,0 +1,18 @@ +using Prism.Commands; +using Prism.Mvvm; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; + +namespace PlixP.ViewModels +{ + public class MovieItemViewModel : BindableBase + { + + public MovieItemViewModel() + { + + } + } +} diff --git a/PlixP/ViewModels/MovieListViewModel.cs b/PlixP/ViewModels/MovieListViewModel.cs new file mode 100644 index 0000000..82894b0 --- /dev/null +++ b/PlixP/ViewModels/MovieListViewModel.cs @@ -0,0 +1,195 @@ +using Prism.Commands; +using Prism.Mvvm; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Input; +using HandyControl.Controls; +using MaterialDesignThemes.Wpf; +using PlixP.Models; +using PlixP.Repositories.Contracts; +using PlixP.Services.Contracts; +using PlixP.Views.Dialogs; +using MessageBox = HandyControl.Controls.MessageBox; + +namespace PlixP.ViewModels +{ + public class MovieListViewModel : BindableBase + { + private readonly IServiceWrapper _serviceWrapper; + private readonly IRepositoryWrapper _repositoryWrapper; + private int pagingCount = 25; + private int currentPage = 0; + public bool IsAllActive { get; set; } = true; + public MovieListModel MovieListModel { get; set; } = new MovieListModel(); + public ICommand RefreshCommand { get; set; } + public ICommand CheckByRealease { get; set; } + public ICommand MovieSelectedCommand { get; set; } + public ICommand RefreshFilesCommand { get; set; } + public ICommand CheckByAddedCommand { get; set; } + public ICommand CheckByImdbCommand { get; set; } + public ICommand GenreSelectedCommand { get; set; } + public ICommand NextPageCommand { get; set; } + public ICommand SearchCommand { get; set; } + + public MovieListViewModel(IServiceWrapper serviceWrapper, IRepositoryWrapper repositoryWrapper) + { + _serviceWrapper = serviceWrapper; + _repositoryWrapper = repositoryWrapper; + Initialize(); + CheckByImdbCommand = new DelegateCommand(async () => + { + ChangeByImdb(); + }); + RefreshCommand = new DelegateCommand(async () => + { + //await _serviceWrapper.MovieServices.SyncMovies(); + await Initialize(); + IsAllActive = true; + }); + + MovieSelectedCommand = new DelegateCommand((dataContext) => + { + Dialog.Show(new MovieDialog() { DataContext = new MovieDialogViewModel(serviceWrapper, repositoryWrapper, dataContext) }); + }); + + RefreshFilesCommand = new DelegateCommand(() => + { + serviceWrapper.LocalServices.ResetFolder(); + }); + + CheckByAddedCommand = new DelegateCommand(async () => + { + await Initialize(); + }); + + CheckByRealease = new DelegateCommand(() => + { + ChangeByRelease(); + }); + + GenreSelectedCommand = new DelegateCommand(() => + { + var genreDialog = new GenresDialog(_serviceWrapper,_repositoryWrapper); + genreDialog.GenreSelected += (async (ss, genre) => + { + await ChangeGenre(genre); + }); + Dialog.Show(genreDialog); + }); + SearchCommand = new DelegateCommand(async (search) => + { + try + { + MovieListModel.UnPagedMovies.Clear(); + foreach (var originalMovie in MovieListModel.OriginalMovies) + { + if (originalMovie.Title.ToLower().Contains(search.ToLower())) + MovieListModel.UnPagedMovies.Add(originalMovie); + } + MovieListModel.PageMovies.Clear(); + currentPage = 0; + Pagination(); + } + catch (Exception e) + { + MessageBox.Show(e.Message); + await Initialize(); + } + }); + NextPageCommand = new DelegateCommand(async () => + { + currentPage++; + Pagination(); + }); + + } + + private void ChangeByImdb() + { + MovieListModel.UnPagedMovies.Clear(); + MovieListModel.OriginalMovies.OrderByDescending(m => + { + float rate; + if (float.TryParse(m.imdbRating, out rate)) + return rate; + return 0; + }).ToList() + .ForEach(m => MovieListModel.UnPagedMovies.Add(m)); + MovieListModel.PageMovies.Clear(); + currentPage = 0; + Pagination(); + } + + private async Task ChangeGenre(List genres) + { + MovieListModel.UnPagedMovies.Clear(); + foreach (var genre in genres) + { + var moviesGenre = await _serviceWrapper.MovieServices.SelectByGenre(genre); + moviesGenre.ForEach(m => MovieListModel.UnPagedMovies.Add(m)); + } + MovieListModel.PageMovies.Clear(); + currentPage = 0; + } + private async Task ChangeGenre(string genre) + { + MovieListModel.UnPagedMovies.Clear(); + var moviesGenre = await _serviceWrapper.MovieServices.SelectByGenre(genre); + moviesGenre.ForEach(m => MovieListModel.UnPagedMovies.Add(m)); + MovieListModel.PageMovies.Clear(); + currentPage = 0; + } + + private async Task ChangeByRelease() + { + MovieListModel.UnPagedMovies.Clear(); + MovieListModel.OriginalMovies.OrderByDescending(m => m.IntYear).ToList() + .ForEach(m => MovieListModel.UnPagedMovies.Add(m)); + MovieListModel.PageMovies.Clear(); + currentPage = 0; + Pagination(); + } + + private async Task Initialize() + { + MovieListModel.PageMovies.Clear(); + MovieListModel.UnPagedMovies.Clear(); + MovieListModel.OriginalMovies.Clear(); + (await _serviceWrapper.MovieServices.GetMovies()) + .OrderByDescending(m => m.CreationDate) + .ToList() + .ForEach(m => + { + MovieListModel.OriginalMovies.Add(m); + }); + + MovieListModel.OriginalMovies.ToList() + .ForEach(m => MovieListModel.UnPagedMovies.Add(m)); + MovieListModel.OriginalCategories = new ObservableCollection(_repositoryWrapper + .SetRepository().TableNoTracking); + MovieListModel.MoviesCount = MovieListModel.UnPagedMovies.Count; + MovieListModel.OriginalGenres = await _serviceWrapper.MovieServices.GetGenres(); + currentPage = 0; + Pagination(); + } + + public void Pagination() + { + var selected = MovieListModel.UnPagedMovies.Skip((currentPage * pagingCount)).Take(pagingCount).ToList(); + foreach (var movie in selected) + { + Application.Current.Dispatcher.Invoke(() => + { + MovieListModel.PageMovies.Add(movie); + }); + } + } + + } +} diff --git a/PlixP/ViewModels/SuggestionBoxViewModel.cs b/PlixP/ViewModels/SuggestionBoxViewModel.cs new file mode 100644 index 0000000..da52465 --- /dev/null +++ b/PlixP/ViewModels/SuggestionBoxViewModel.cs @@ -0,0 +1,16 @@ +using Prism.Commands; +using Prism.Mvvm; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace PlixP.ViewModels +{ + public class SuggestionBoxViewModel : BindableBase + { + public SuggestionBoxViewModel() + { + + } + } +} diff --git a/PlixP/Views/Dialogs/AddCategoryDialog.xaml b/PlixP/Views/Dialogs/AddCategoryDialog.xaml new file mode 100644 index 0000000..4d7b9aa --- /dev/null +++ b/PlixP/Views/Dialogs/AddCategoryDialog.xaml @@ -0,0 +1,15 @@ + + + diff --git a/PlixP/Views/Dialogs/AddCategoryDialog.xaml.cs b/PlixP/Views/Dialogs/AddCategoryDialog.xaml.cs new file mode 100644 index 0000000..0e9ff54 --- /dev/null +++ b/PlixP/Views/Dialogs/AddCategoryDialog.xaml.cs @@ -0,0 +1,15 @@ +using System.Windows.Controls; + +namespace PlixP.Views.Dialogs +{ + /// + /// Interaction logic for AddCategoryDialog + /// + public partial class AddCategoryDialog : UserControl + { + public AddCategoryDialog() + { + InitializeComponent(); + } + } +} diff --git a/PlixP/Views/Dialogs/GenresDialog.xaml b/PlixP/Views/Dialogs/GenresDialog.xaml new file mode 100644 index 0000000..3d2ad28 --- /dev/null +++ b/PlixP/Views/Dialogs/GenresDialog.xaml @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PlixP/Views/Dialogs/GenresDialog.xaml.cs b/PlixP/Views/Dialogs/GenresDialog.xaml.cs new file mode 100644 index 0000000..e951385 --- /dev/null +++ b/PlixP/Views/Dialogs/GenresDialog.xaml.cs @@ -0,0 +1,39 @@ +using System; +using System.Windows.Controls; +using System.Windows.Input; +using HandyControl.Controls; +using HandyControl.Interactivity; +using PlixP.Repositories.Contracts; +using PlixP.Services.Contracts; +using PlixP.ViewModels; +using Card = MaterialDesignThemes.Wpf.Card; + +namespace PlixP.Views.Dialogs +{ + /// + /// Interaction logic for GenresDialog + /// + public partial class GenresDialog : UserControl + { + public event EventHandler GenreSelected; + public GenresDialog(IServiceWrapper serviceWrapper,IRepositoryWrapper repositoryWrapper) + { + var viewModel = new GenresDialogViewModel(serviceWrapper, repositoryWrapper); + DataContext = viewModel; + viewModel.GenreSelected += ((model, genre) => + { + GenreSelected?.Invoke(model,genre); + ControlCommands.Close.Execute(this,this); + }); + + + InitializeComponent(); + } + + private void GenreItem_OnMouseDown(object sender, MouseButtonEventArgs e) + { + if(sender is Card card && card.DataContext is GenreItemModel model && DataContext is GenresDialogViewModel viewModel) + viewModel.GenreSelectCommand.Execute(model); + } + } +} diff --git a/PlixP/Views/Dialogs/LoadingDialog.xaml b/PlixP/Views/Dialogs/LoadingDialog.xaml new file mode 100644 index 0000000..13c397b --- /dev/null +++ b/PlixP/Views/Dialogs/LoadingDialog.xaml @@ -0,0 +1,34 @@ + + + + + + + + + diff --git a/PlixP/Views/Dialogs/LoadingDialog.xaml.cs b/PlixP/Views/Dialogs/LoadingDialog.xaml.cs new file mode 100644 index 0000000..4d89451 --- /dev/null +++ b/PlixP/Views/Dialogs/LoadingDialog.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace PlixP.Views.Dialogs +{ + /// + /// Interaction logic for LoadingDialog.xaml + /// + public partial class LoadingDialog : UserControl + { + public LoadingDialog() + { + InitializeComponent(); + } + } +} diff --git a/PlixP/Views/Dialogs/MovieDialog.xaml b/PlixP/Views/Dialogs/MovieDialog.xaml new file mode 100644 index 0000000..4e5cbd7 --- /dev/null +++ b/PlixP/Views/Dialogs/MovieDialog.xaml @@ -0,0 +1,337 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PlixP/Views/Dialogs/MovieDialog.xaml.cs b/PlixP/Views/Dialogs/MovieDialog.xaml.cs new file mode 100644 index 0000000..cae90be --- /dev/null +++ b/PlixP/Views/Dialogs/MovieDialog.xaml.cs @@ -0,0 +1,143 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using MaterialDesignThemes.Wpf; +using Microsoft.EntityFrameworkCore; +using PlixP.Models; +using PlixP.ViewModels; + +namespace PlixP.Views.Dialogs +{ + /// + /// Interaction logic for MovieDialog + /// + public partial class MovieDialog : UserControl + { + public Movie Movie + { + get + { + return (DataContext as MovieDialogViewModel).Movie; + } + } + + public MovieDialog() + { + InitializeComponent(); + seenToggle.Checked += SeenToggle_OnChecked; + dubbedToggle.Checked += DubbedToggle_OnChecked; + } + public MovieDialog(Movie dataContext) + { + InitializeComponent(); + } + + + private void Edit_OnClick(object sender, RoutedEventArgs e) + { + var button = sender as Button; + button.Content = new PackIcon + { + Kind = PackIconKind.Check, + Width = 20 + }; + categoryItems.IsEnabled = true; + button.Click += AcceptEdit_Click; + titleTextBox.IsReadOnly = false; + dubbedToggle.IsEnabled = true; + seenToggle.IsEnabled = true; + addCategoryButton.IsEnabled = true; + } + + private void AcceptEdit_Click(object sender, RoutedEventArgs e) + { + try + { + Movie newMovie = (DataContext as MovieDialogViewModel)?.Movie.Clone() as Movie; + if (Movie.Title != null && !Movie.Title.Equals(titleTextBox.Text)) + { + newMovie.Title = titleTextBox.Text; + } + if (Movie.IsDubbed != dubbedToggle.IsChecked) + { + if (dubbedToggle.IsChecked == true) + { + var newName = Movie.FileName.Split('.'); + string nn = ""; + for (int i = 0; i < newName.Length; i++) + { + if (i == newName.Length - 1) + { + nn += "Dubbed" + "."; + } + + nn += newName[i] + "."; + } + newMovie.FileName = nn; + } + else + { + var newName = Movie.FileName.Replace("Dubbed.", ""); + newMovie.FileName = newName; + } + } + if (Movie.IsSeen != seenToggle.IsChecked) + { + newMovie.IsSeen = seenToggle.IsChecked ?? false; + } + + (DataContext as MovieDialogViewModel)?.SaveCommand.Execute(newMovie); + } + + catch (Exception exception) + { + MessageBox.Show(exception.Message); + } + } + + private void DubbedToggle_OnChecked(object sender, RoutedEventArgs e) + { + var toggel = sender as CheckBox; + if (!categoryItems.IsEnabled) + toggel.IsChecked = !toggel.IsChecked; + } + + private void SeenToggle_OnChecked(object sender, RoutedEventArgs e) + { + var toggel = sender as CheckBox; + if (!categoryItems.IsEnabled) + toggel.IsChecked = !toggel.IsChecked; + } + + private void AddCategoryButtonBase_OnClick(object sender, RoutedEventArgs e) + { + if (!AddCategoryPopUp.IsPopupOpen) + AddCategoryPopUp.IsPopupOpen = true; + else + AddCategoryPopUp.IsPopupOpen = false; + } + + private void CategoryNameTextBox_OnTextChanged(object sender, TextChangedEventArgs e) + { + categoryCombo.IsEnabled = true; + } + + private void AddCategoryButton_OnClick(object sender, RoutedEventArgs e) + { + if (categoryCombo.SelectedValue != null) + { + var category = categoryCombo.SelectedValue as Category; + (DataContext as MovieDialogViewModel)?.AddCategoryCommand.Execute(category); + } + } + + private void RemoveCategoryButton_OnClick(object sender, RoutedEventArgs e) + { + (DataContext as MovieDialogViewModel)?.RemoveCategoryCommand.Execute((sender as Button).DataContext); + } + } +} diff --git a/PlixP/Views/Dialogs/QuestionDialog.xaml b/PlixP/Views/Dialogs/QuestionDialog.xaml new file mode 100644 index 0000000..7f703a2 --- /dev/null +++ b/PlixP/Views/Dialogs/QuestionDialog.xaml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + diff --git a/PlixP/Views/MainWindow.xaml.cs b/PlixP/Views/MainWindow.xaml.cs new file mode 100644 index 0000000..2fc58ff --- /dev/null +++ b/PlixP/Views/MainWindow.xaml.cs @@ -0,0 +1,25 @@ +using System; +using System.Windows; +using MaterialDesignThemes.Wpf; +using PlixP.Views.Dialogs; +using Prism.Regions; +using Window = HandyControl.Controls.Window; + +namespace PlixP.Views +{ + /// + /// Interaction logic for MainWindow.xaml + /// + public partial class MainWindow : Window + { + public static Snackbar snackbar = new Snackbar(); + public MainWindow(IRegionManager regionManager) + { + InitializeComponent(); + DialogHost.Show(new LoadingDialog()); + regionManager.RegisterViewWithRegion("ContentRegion", typeof(MovieList)); + mainGrid.Children.Add(snackbar); + } + + } +} diff --git a/PlixP/Views/MasterDetail.xaml b/PlixP/Views/MasterDetail.xaml new file mode 100644 index 0000000..26a9cac --- /dev/null +++ b/PlixP/Views/MasterDetail.xaml @@ -0,0 +1,173 @@ + + + + + + + + + + + + + Path + + + + + + + + + + + + + + + + + + + + + + Add Category + + + + + + + + + + + + + + + + + + diff --git a/PlixP/Views/MasterDetail.xaml.cs b/PlixP/Views/MasterDetail.xaml.cs new file mode 100644 index 0000000..ddbe995 --- /dev/null +++ b/PlixP/Views/MasterDetail.xaml.cs @@ -0,0 +1,34 @@ +using System.Windows; +using System.Windows.Controls; +using PlixP.ViewModels; + +namespace PlixP.Views +{ + /// + /// Interaction logic for MasterDetail + /// + public partial class MasterDetail : UserControl + { + public MasterDetail() + { + InitializeComponent(); + } + + private void Path_OnClick(object sender, RoutedEventArgs e) + { + (DataContext as MasterDetailViewModel)?.PathCommand.Execute(addPathTextBox.Text); + } + + private void Cat_OnClick(object sender, RoutedEventArgs e) + { + (DataContext as MasterDetailViewModel)?.AddCategoryCommand.Execute(addCategoryTextBox.Text); + } + + + private void RemovePathButtonBase_OnClick(object sender, RoutedEventArgs e) + { + if(sender is Button button && button.DataContext is string str && DataContext is MasterDetailViewModel viewModel) + viewModel.RemovePathCommand.Execute(str); + } + } +} diff --git a/PlixP/Views/MovieList.xaml b/PlixP/Views/MovieList.xaml new file mode 100644 index 0000000..13f73a7 --- /dev/null +++ b/PlixP/Views/MovieList.xaml @@ -0,0 +1,198 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Seach + + + + +