Skip to content

Commit

Permalink
Merge pull request #32 from insomniachi/media_detection
Browse files Browse the repository at this point in the history
Media detection
  • Loading branch information
insomniachi authored Jun 24, 2023
2 parents 2392a1d + b65ccd8 commit 43e56d4
Show file tree
Hide file tree
Showing 46 changed files with 1,130 additions and 47 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -354,3 +354,4 @@ MigrationBackup/
/Plugins/Totoro.Plugins.dll
/Plugins/Totoro.Plugins.Anime.dll
/Plugins/Totoro.Plugins.Torrents.dll
/Plugins/Totoro.Plugins.MediaDetection.dll
Binary file not shown.
Binary file added Plugins/Totoro.Plugins.MediaDetection.Vlc.dll
Binary file not shown.
Binary file not shown.
2 changes: 1 addition & 1 deletion Totoro.Core.Tests/Services/AnimeIdServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public async void GetId_ReturnsValue(ListServiceType tracker, long id)
AniList = 12189,
Kitsu = 6686
};
var service = new AnimeIdService(_httpClient, Mock.Of<ISettings>());
var service = new AnimeIdService(Mock.Of<ISettings>());

// act
try
Expand Down
3 changes: 3 additions & 0 deletions Totoro.Core/Contracts/IToastService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@
public interface IToastService
{
void DownloadCompleted(string directory, string name);
public void CheckEpisodeComplete(AnimeModel anime, int currentEp);
void Playing(AnimeModel anime, string episode);
void PromptAnimeSelection(IEnumerable<AnimeModel> items, AnimeModel defaultSelection);
}
}
1 change: 1 addition & 0 deletions Totoro.Core/Contracts/IViewService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public interface IViewService
Task PlayVideo(string title, string url);
Task<T> SelectModel<T>(IEnumerable<T> models, T defaultValue = default, Func<string, IObservable<IEnumerable<T>>> searcher = default) where T : class;
Task<long?> TryGetId(string title);
Task<long?> BeginTryGetId(string title);
Task SubmitTimeStamp(long malId, int ep, VideoStreamModel stream, AniSkipResult existingResult, double duration, double introStart);
Task<bool> Question(string title, string message);
Task<Unit> Information(string title, string message);
Expand Down
21 changes: 21 additions & 0 deletions Totoro.Core/Models/Tracking.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,25 @@ public record Tracking
public DateTime? StartDate { get; set; }
public DateTime? FinishDate { get; set; }
public DateTime? UpdatedAt { get; set; }

public static Tracking Next(AnimeModel anime)
{
var tracking = new Tracking
{
WatchedEpisodes = (anime.Tracking?.WatchedEpisodes ?? 0) + 1
};

if (tracking.WatchedEpisodes == anime.TotalEpisodes)
{
tracking.Status = AnimeStatus.Completed;
tracking.FinishDate = DateTime.Today;
}
else if(tracking.WatchedEpisodes == 1)
{
tracking.Status = AnimeStatus.Watching;
tracking.StartDate = DateTime.Today;
}

return tracking;
}
}
3 changes: 0 additions & 3 deletions Totoro.Core/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,6 @@ public static IServiceCollection AddPlugins(this IServiceCollection services)
PluginFactory<ITorrentTracker>.Instance.LoadPlugin(new Plugins.Torrents.AnimeTosho.Plugin());

#endif
services.AddSingleton<IPluginManager>(x => new PluginManager(x.GetRequiredService<HttpClient>(),
PluginFactory<AnimeProvider>.Instance,
PluginFactory<ITorrentTracker>.Instance));
services.AddSingleton(typeof(IPluginOptionsStorage<>), typeof(PluginOptionStorage<>));
services.AddSingleton<PluginOptionsStorage>();

Expand Down
2 changes: 1 addition & 1 deletion Totoro.Core/Services/AniList/AniListService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public IObservable<IEnumerable<AnimeModel>> GetAnime(string name)
var response = await _anilistClient.SendQueryAsync<Query>(new GraphQL.GraphQLRequest
{
Query = new QueryQueryBuilder().WithPage(new PageQueryBuilder()
.WithMedia(MediaQueryBuilder(), search: name, type: MediaType.Anime), page: 1, perPage: 10).Build()
.WithMedia(MediaQueryBuilder(), search: name, type: MediaType.Anime), page: 1, perPage: 5).Build()
});

observer.OnNext(response.Data.Page.Media.Where(FilterNsfw).Select(ConvertModel));
Expand Down
8 changes: 1 addition & 7 deletions Totoro.Core/Services/DiscordRichPresense.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,5 @@ public void UpdateImage(string url)
}
}
public void ClearTimer() => _client.UpdateClearTime();
public void Clear()
{
_client.UpdateDetails("Idle");
_client.UpdateState(string.Empty);
_client.UpdateLargeAsset("icon");
ClearTimer();
}
public void Clear() => SetPresence();
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Totoro.Core.Services.MediaEvents;

internal class DiscordRichPresenseUpdater : MediaEventListener
public class DiscordRichPresenseUpdater : MediaEventListener
{
private readonly IDiscordRichPresense _discordRichPresense;
private readonly ISettings _settings;
Expand Down
16 changes: 10 additions & 6 deletions Totoro.Core/Services/MediaEvents/TrackingUpdater.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ public interface ITrackingUpdater
public event EventHandler TrackingUpdated;
}

internal class TrackingUpdater : MediaEventListener, ITrackingUpdater
public class TrackingUpdater : MediaEventListener, ITrackingUpdater
{
private readonly ITrackingServiceContext _trackingService;
private readonly ISettings _settings;
Expand All @@ -16,6 +16,7 @@ internal class TrackingUpdater : MediaEventListener, ITrackingUpdater
private TimeSpan _duration;
private static readonly TimeSpan _nextBuffer = TimeSpan.FromMinutes(3);
private bool _isUpdated;
private readonly object _lock = new object();

public event EventHandler TrackingUpdated;

Expand Down Expand Up @@ -48,13 +49,16 @@ protected override void OnTimestampsChanged()

protected override void OnPositionChanged(TimeSpan position)
{
_position = position;
if (!IsEnabled || position < _updateAt || _isUpdated || _animeModel.Tracking?.WatchedEpisodes >= _currentEpisode)
lock(_lock)
{
return;
}
_position = position;
if (!IsEnabled || position < _updateAt || _isUpdated || _animeModel.Tracking?.WatchedEpisodes >= _currentEpisode)
{
return;
}

_ = UpdateTracking();
_ = UpdateTracking();
}
}

protected override void OnNextTrack()
Expand Down
17 changes: 17 additions & 0 deletions Totoro.Plugins.MediaDetection.Mpv/MediaPlayer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace Totoro.Plugins.MediaDetection.Generic;

public class Mpv : GenericMediaPlayer
{
protected override string ParseFromWindowTitle(string windowTitle)
{
return windowTitle.Replace("- mpv", string.Empty);
}
}

public class MpcHc : GenericMediaPlayer
{
public MpcHc()
{
GetTitleFromWindow = true;
}
}
45 changes: 45 additions & 0 deletions Totoro.Plugins.MediaDetection.Mpv/Plugin.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System.Reflection;
using Totoro.Plugins.Contracts;
using Totoro.Plugins.MediaDetection.Contracts;
using Totoro.Plugins.Options;

namespace Totoro.Plugins.MediaDetection.Generic;

public abstract class GenericPlugin<T> : IPlugin<INativeMediaPlayer>
where T : INativeMediaPlayer, new()
{
public INativeMediaPlayer Create() => new T();

public abstract PluginInfo GetInfo();
public PluginOptions GetOptions() => new();
public void SetOptions(PluginOptions options) { }
object IPlugin.Create() => Create();
}

public class MpvPlugin : GenericPlugin<Mpv>
{
public override PluginInfo GetInfo()
{
return new PluginInfo
{
Name = "mpv",
DisplayName = "MPV",
Description = "",
Version = Assembly.GetExecutingAssembly().GetName().Version!
};
}
}

public class MpcHcPlugin : GenericPlugin<MpcHc>
{
public override PluginInfo GetInfo()
{
return new PluginInfo
{
Name = "mpc-hc64",
DisplayName = "MPC-HC",
Description = "",
Version = Assembly.GetExecutingAssembly().GetName().Version!
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Platforms>x64</Platforms>
<Version>1.0</Version>
<OutputPath Condition="$(Configuration) == Release">..\Plugins\</OutputPath>
<AppendTargetFrameworkToOutputPath Condition="$(Configuration) == Release">false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath Condition="$(Configuration) == Release">false</AppendRuntimeIdentifierToOutputPath>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Totoro.Plugins.MediaDetection\Totoro.Plugins.MediaDetection.csproj" />
</ItemGroup>

</Project>
124 changes: 124 additions & 0 deletions Totoro.Plugins.MediaDetection.Vlc/MediaPlayer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
using System.Diagnostics;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Text.RegularExpressions;
using FlaUI.Core;
using FlaUI.Core.AutomationElements;
using FlaUI.Core.Definitions;
using FlaUI.UIA3;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using Totoro.Plugins.MediaDetection.Contracts;

namespace Totoro.Plugins.MediaDetection.Vlc
{
internal sealed partial class MediaPlayer : ReactiveObject, INativeMediaPlayer, IHavePosition
{
private Application? _application;
private Window? _mainWindow;
private Slider? _slider;
private readonly Subject<TimeSpan> _positionChanged = new();

[Reactive] TimeSpan Duration { get; set; }
public IObservable<TimeSpan> PositionChanged => _positionChanged;
public IObservable<TimeSpan> DurationChanged { get; }
public Process? Process { get; private set; }

public MediaPlayer()
{
DurationChanged = this.WhenAnyValue(x => x.Duration);
}

public string GetTitle()
{
if (_mainWindow is null)
{
return "";
}

var title = _mainWindow.Title;

return title.Replace("- Vlc media player", string.Empty).Trim();
}

public void Initialize(string fileName)
{
_application = Application.Launch("", fileName);
InitializeInternal();
}


public void Initialize(Window window)
{
_mainWindow = window;
Process = Process.GetProcessById(window.Properties.ProcessId);
InitializeInternal();
}

public void Dispose()
{
if(_application is null)
{
return;
}

_application.Dispose();
}

private void InitializeInternal()
{
while(_mainWindow is not { IsAvailable : true })
{
try
{
_mainWindow = _application!.GetMainWindow(new UIA3Automation());
}
catch { }
}

GetDuration();
GetSlider();
}

private void GetDuration()
{
foreach (var item in _mainWindow!.FindAllDescendants(cf => cf.ByControlType(ControlType.Text)))
{
var match = DurationRegex().Match(item.Name);
if (match.Success && item.Patterns.LegacyIAccessible.Pattern.Description.Value.Contains("Total"))
{
var parts = item.Name.Split(':');

if(parts.Length == 3)
{
Duration = new TimeSpan(int.Parse(parts[0]), int.Parse(parts[1]), int.Parse(parts[2]));
}
else
{
Duration = new TimeSpan(0, int.Parse(parts[0]), int.Parse(parts[1]));
}
}
}
}

private void GetSlider()
{
foreach (var item in _mainWindow!.FindAllDescendants(cf => cf.ByControlType(ControlType.Slider)).Select(x => x.AsSlider()))
{
if (!item.Patterns.LegacyIAccessible.Pattern.Description.Value.Contains('%'))
{
_slider = item;
var property = item.Patterns.Value.Pattern.PropertyIds.Value;
var handler = item.RegisterPropertyChangedEvent(TreeScope.Element, (ae, _, _) =>
{
var percent = ae.AsSlider().Value;
_positionChanged.OnNext(TimeSpan.FromSeconds(Duration.TotalSeconds * percent / 10000));
}, property);
}
}
}

[GeneratedRegex(@"(\d)+:(\d)+")]
private static partial Regex DurationRegex();
}
}
28 changes: 28 additions & 0 deletions Totoro.Plugins.MediaDetection.Vlc/Plugin.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System.Reflection;
using Totoro.Plugins.Contracts;
using Totoro.Plugins.MediaDetection.Contracts;
using Totoro.Plugins.Options;

namespace Totoro.Plugins.MediaDetection.Vlc;

public class Plugin : IPlugin<INativeMediaPlayer>
{
public INativeMediaPlayer Create() => new MediaPlayer();

public PluginInfo GetInfo()
{
return new PluginInfo
{
Name = "vlc",
DisplayName = "VLC Media Player",
Description = "",
Version = Assembly.GetExecutingAssembly().GetName().Version!
};
}

public PluginOptions GetOptions() => new();

public void SetOptions(PluginOptions options) { }

object IPlugin.Create() => Create();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Platforms>x64</Platforms>
<Version>1.0</Version>
<OutputPath Condition="$(Configuration) == Release">..\Plugins\</OutputPath>
<AppendTargetFrameworkToOutputPath Condition="$(Configuration) == Release">false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath Condition="$(Configuration) == Release">false</AppendRuntimeIdentifierToOutputPath>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Totoro.Plugins.MediaDetection\Totoro.Plugins.MediaDetection.csproj" />
</ItemGroup>

</Project>
Loading

0 comments on commit 43e56d4

Please sign in to comment.