Compare commits
3 Commits
10.10
...
regex-seas
Author | SHA1 | Date | |
---|---|---|---|
|
f0113691e8 | ||
|
983d1e346b | ||
|
700c025fef |
@ -33,7 +33,11 @@ public class ChapterAnalyzer(ILogger<ChapterAnalyzer> logger) : IMediaFileAnalyz
|
||||
AnalysisMode mode,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var expression = mode switch
|
||||
var expression = Plugin.Instance!.GetSeasonRegex(analysisQueue[0].SeasonId, mode);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(expression))
|
||||
{
|
||||
expression = mode switch
|
||||
{
|
||||
AnalysisMode.Introduction => _config.ChapterAnalyzerIntroductionPattern,
|
||||
AnalysisMode.Credits => _config.ChapterAnalyzerEndCreditsPattern,
|
||||
@ -41,6 +45,7 @@ public class ChapterAnalyzer(ILogger<ChapterAnalyzer> logger) : IMediaFileAnalyz
|
||||
AnalysisMode.Preview => _config.ChapterAnalyzerPreviewPattern,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(mode), $"Unexpected analysis mode: {mode}")
|
||||
};
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(expression))
|
||||
{
|
||||
|
@ -471,6 +471,12 @@
|
||||
<option value="None">None</option>
|
||||
</select>
|
||||
</label>
|
||||
<label for="regexRecap" style="margin-right: 1.5em; display: inline-block">
|
||||
<span>Recap Chapter Regex</span>
|
||||
<input type="text" id="regexRecap" class="emby-input" />
|
||||
</label>
|
||||
</label>
|
||||
<br />
|
||||
<label for="actionIntro" style="margin-right: 1.5em; display: inline-block">
|
||||
<span>Introduction analysis</span>
|
||||
<select is="emby-select" id="actionIntro" class="emby-select-withcolor emby-select">
|
||||
@ -480,6 +486,11 @@
|
||||
<option value="None">None</option>
|
||||
</select>
|
||||
</label>
|
||||
<label for="regexIntro" style="margin-right: 1.5em; display: inline-block">
|
||||
<span>Introduction Chapter Regex</span>
|
||||
<input type="text" id="regexIntro" class="emby-input" />
|
||||
</label>
|
||||
<br />
|
||||
<label for="actionCredits" style="margin-right: 1.5em; display: inline-block">
|
||||
<span>Credits (Outro) analysis</span>
|
||||
<select is="emby-select" id="actionCredits" class="emby-select-withcolor emby-select">
|
||||
@ -490,6 +501,11 @@
|
||||
<option value="None">None</option>
|
||||
</select>
|
||||
</label>
|
||||
<label for="regexCredits" style="margin-right: 1.5em; display: inline-block">
|
||||
<span>Credits (Outro) Chapter Regex</span>
|
||||
<input type="text" id="regexCredits" class="emby-input" />
|
||||
</label>
|
||||
<br />
|
||||
<label for="actionPreview" style="margin-right: 1.5em; display: inline-block">
|
||||
<span>Preview analysis</span>
|
||||
<select is="emby-select" id="actionPreview" class="emby-select-withcolor emby-select">
|
||||
@ -498,6 +514,10 @@
|
||||
<option value="None">None</option>
|
||||
</select>
|
||||
</label>
|
||||
<label for="regexPreview" style="margin-right: 1.5em; display: inline-block">
|
||||
<span>Preview Chapter Regex</span>
|
||||
<input type="text" id="regexPreview" class="emby-input" />
|
||||
</label>
|
||||
</div>
|
||||
<br />
|
||||
|
||||
@ -797,9 +817,13 @@
|
||||
// visualizer elements
|
||||
var analyzerActionsSection = document.querySelector("div#analyzerActionsSection");
|
||||
var actionIntro = analyzerActionsSection.querySelector("select#actionIntro");
|
||||
var regexIntro = analyzerActionsSection.querySelector("input#regexIntro");
|
||||
var actionCredits = analyzerActionsSection.querySelector("select#actionCredits");
|
||||
var regexCredits = analyzerActionsSection.querySelector("input#regexCredits");
|
||||
var actionRecap = analyzerActionsSection.querySelector("select#actionRecap");
|
||||
var regexRecap = analyzerActionsSection.querySelector("input#regexRecap");
|
||||
var actionPreview = analyzerActionsSection.querySelector("select#actionPreview");
|
||||
var regexPreview = analyzerActionsSection.querySelector("input#regexPreview");
|
||||
var saveAnalyzerActionsButton = analyzerActionsSection.querySelector("button#saveAnalyzerActions");
|
||||
var canvas = document.querySelector("canvas#troubleshooter");
|
||||
var selectShow = document.querySelector("select#troubleshooterShow");
|
||||
@ -1082,6 +1106,11 @@
|
||||
actionCredits.value = analyzerActions.Credits || "Default";
|
||||
actionRecap.value = analyzerActions.Recap || "Default";
|
||||
actionPreview.value = analyzerActions.Preview || "Default";
|
||||
const analyzerRegexs = await getJson("Intros/AnalyzerRegexs/" + encodeURI(selectSeason.value));
|
||||
regexIntro.value = analyzerRegexs.Introduction || "";
|
||||
regexCredits.value = analyzerRegexs.Credits || "";
|
||||
regexRecap.value = analyzerRegexs.Recap || "";
|
||||
regexPreview.value = analyzerRegexs.Preview || "";
|
||||
analyzerActionsSection.style.display = "unset";
|
||||
|
||||
// show the erase season button
|
||||
@ -1539,7 +1568,7 @@
|
||||
saveAnalyzerActionsButton.addEventListener("click", () => {
|
||||
Dashboard.showLoadingMsg();
|
||||
|
||||
var url = "Intros/AnalyzerActions/UpdateSeason";
|
||||
var url1 = "Intros/AnalyzerActions/UpdateSeason";
|
||||
const actions = {
|
||||
id: selectSeason.value,
|
||||
analyzerActions: {
|
||||
@ -1550,7 +1579,20 @@
|
||||
},
|
||||
};
|
||||
|
||||
fetchWithAuth(url, "POST", JSON.stringify(actions));
|
||||
var url2 = "Intros/AnalyzerRegexs/UpdateSeason";
|
||||
|
||||
const regexs = {
|
||||
id: selectSeason.value,
|
||||
regexs: {
|
||||
Introduction: regexIntro.value,
|
||||
Credits: regexCredits.value,
|
||||
Recap: regexRecap.value,
|
||||
Preview: regexPreview.value,
|
||||
},
|
||||
};
|
||||
|
||||
fetchWithAuth(url1, "POST", JSON.stringify(actions));
|
||||
fetchWithAuth(url2, "POST", JSON.stringify(regexs));
|
||||
|
||||
Dashboard.alert("Analyzer actions updated for " + selectSeason.value + " of " + selectShow.value);
|
||||
Dashboard.hideLoadingMsg();
|
||||
|
@ -105,6 +105,28 @@ public class VisualizationController(ILogger<VisualizationController> logger, Me
|
||||
return Ok(analyzerActions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the analyzer actions for the provided season.
|
||||
/// </summary>
|
||||
/// <param name="seasonId">Season ID.</param>
|
||||
/// <returns>List of episode titles.</returns>
|
||||
[HttpGet("AnalyzerRegexs/{SeasonId}")]
|
||||
public ActionResult<IReadOnlyDictionary<AnalysisMode, string>> GetSeasonRegexs([FromRoute] Guid seasonId)
|
||||
{
|
||||
if (!Plugin.Instance!.QueuedMediaItems.ContainsKey(seasonId))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var seasonRegexs = new Dictionary<AnalysisMode, string>();
|
||||
foreach (var mode in Enum.GetValues<AnalysisMode>())
|
||||
{
|
||||
seasonRegexs[mode] = Plugin.Instance!.GetSeasonRegex(seasonId, mode);
|
||||
}
|
||||
|
||||
return Ok(seasonRegexs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the names and unique identifiers of all episodes in the provided season.
|
||||
/// </summary>
|
||||
@ -227,6 +249,19 @@ public class VisualizationController(ILogger<VisualizationController> logger, Me
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the analyzer regexs for the provided season.
|
||||
/// </summary>
|
||||
/// <param name="request">Update analyzer regexs request.</param>
|
||||
/// <returns>No content.</returns>
|
||||
[HttpPost("AnalyzerRegexs/UpdateSeason")]
|
||||
public async Task<ActionResult> UpdateAnalyzerRegexs([FromBody] UpdateSeasonRegexRequest request)
|
||||
{
|
||||
await Plugin.Instance!.SetSeasonRegexAsync(request.Id, request.SeasonRegexs).ConfigureAwait(false);
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
private static string GetProductionYear(Guid seriesId)
|
||||
{
|
||||
return seriesId == Guid.Empty
|
||||
|
21
IntroSkipper/Data/UpdateSeasonRegexRequest.cs
Normal file
21
IntroSkipper/Data/UpdateSeasonRegexRequest.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace IntroSkipper.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// /// Update analyzer actions request.
|
||||
/// </summary>
|
||||
public class UpdateSeasonRegexRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets season ID.
|
||||
/// </summary>
|
||||
public Guid Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets analyzer actions.
|
||||
/// </summary>
|
||||
public IReadOnlyDictionary<AnalysisMode, string> SeasonRegexs { get; set; } = new Dictionary<AnalysisMode, string>();
|
||||
}
|
||||
}
|
@ -22,12 +22,14 @@ public class DbSeasonInfo
|
||||
/// <param name="mode">Analysis mode.</param>
|
||||
/// <param name="action">Analyzer action.</param>
|
||||
/// <param name="episodeIds">Episode IDs.</param>
|
||||
public DbSeasonInfo(Guid seasonId, AnalysisMode mode, AnalyzerAction action, IEnumerable<Guid>? episodeIds = null)
|
||||
/// <param name="regex">Regex.</param>
|
||||
public DbSeasonInfo(Guid seasonId, AnalysisMode mode, AnalyzerAction action, IEnumerable<Guid>? episodeIds = null, string? regex = null)
|
||||
{
|
||||
SeasonId = seasonId;
|
||||
Type = mode;
|
||||
Action = action;
|
||||
EpisodeIds = episodeIds ?? [];
|
||||
Regex = regex ?? string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -56,4 +58,9 @@ public class DbSeasonInfo
|
||||
/// Gets the season number.
|
||||
/// </summary>
|
||||
public IEnumerable<Guid> EpisodeIds { get; private set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets the season number.
|
||||
/// </summary>
|
||||
public string Regex { get; private set; } = string.Empty;
|
||||
}
|
||||
|
@ -99,6 +99,9 @@ public class IntroSkipperDbContext : DbContext
|
||||
(c1, c2) => (c1 ?? new List<Guid>()).SequenceEqual(c2 ?? new List<Guid>()),
|
||||
c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())),
|
||||
c => c.ToList()));
|
||||
|
||||
entity.Property(e => e.Regex)
|
||||
.HasDefaultValue(string.Empty);
|
||||
});
|
||||
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
79
IntroSkipper/Migrations/20241125172633_SeasonRegex.Designer.cs
generated
Normal file
79
IntroSkipper/Migrations/20241125172633_SeasonRegex.Designer.cs
generated
Normal file
@ -0,0 +1,79 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using IntroSkipper.Db;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace IntroSkipper.Migrations
|
||||
{
|
||||
[DbContext(typeof(IntroSkipperDbContext))]
|
||||
[Migration("20241125172633_SeasonRegex")]
|
||||
partial class SeasonRegex
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.11");
|
||||
|
||||
modelBuilder.Entity("IntroSkipper.Db.DbSeasonInfo", b =>
|
||||
{
|
||||
b.Property<Guid>("SeasonId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Action")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasDefaultValue(0);
|
||||
|
||||
b.Property<string>("EpisodeIds")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Regex")
|
||||
.IsRequired()
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT")
|
||||
.HasDefaultValue("");
|
||||
|
||||
b.HasKey("SeasonId", "Type");
|
||||
|
||||
b.HasIndex("SeasonId");
|
||||
|
||||
b.ToTable("DbSeasonInfo", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IntroSkipper.Db.DbSegment", b =>
|
||||
{
|
||||
b.Property<Guid>("ItemId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<double>("End")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("REAL")
|
||||
.HasDefaultValue(0.0);
|
||||
|
||||
b.Property<double>("Start")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("REAL")
|
||||
.HasDefaultValue(0.0);
|
||||
|
||||
b.HasKey("ItemId", "Type");
|
||||
|
||||
b.HasIndex("ItemId");
|
||||
|
||||
b.ToTable("DbSegment", (string)null);
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
29
IntroSkipper/Migrations/20241125172633_SeasonRegex.cs
Normal file
29
IntroSkipper/Migrations/20241125172633_SeasonRegex.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace IntroSkipper.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class SeasonRegex : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Regex",
|
||||
table: "DbSeasonInfo",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
defaultValue: string.Empty);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Regex",
|
||||
table: "DbSeasonInfo");
|
||||
}
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ using System;
|
||||
using IntroSkipper.Db;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
@ -14,7 +15,7 @@ namespace IntroSkipper.Migrations
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.10");
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.11");
|
||||
|
||||
modelBuilder.Entity("IntroSkipper.Db.DbSeasonInfo", b =>
|
||||
{
|
||||
@ -33,6 +34,12 @@ namespace IntroSkipper.Migrations
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Regex")
|
||||
.IsRequired()
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT")
|
||||
.HasDefaultValue("");
|
||||
|
||||
b.HasKey("SeasonId", "Type");
|
||||
|
||||
b.HasIndex("SeasonId");
|
||||
|
@ -308,6 +308,35 @@ public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
|
||||
.ToDictionary(s => s.Type, s => s.EpisodeIds);
|
||||
}
|
||||
|
||||
internal string GetSeasonRegex(Guid id, AnalysisMode mode)
|
||||
{
|
||||
using var db = new IntroSkipperDbContext(_dbPath);
|
||||
return db.DbSeasonInfo.FirstOrDefault(s => s.SeasonId == id && s.Type == mode)?.Regex ?? string.Empty;
|
||||
}
|
||||
|
||||
internal async Task SetSeasonRegexAsync(Guid id, IReadOnlyDictionary<AnalysisMode, string> regexs)
|
||||
{
|
||||
using var db = new IntroSkipperDbContext(_dbPath);
|
||||
var existingEntries = await db.DbSeasonInfo
|
||||
.Where(s => s.SeasonId == id)
|
||||
.ToDictionaryAsync(s => s.Type)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
foreach (var (mode, regex) in regexs)
|
||||
{
|
||||
if (existingEntries.TryGetValue(mode, out var existing))
|
||||
{
|
||||
db.Entry(existing).Property(s => s.Regex).CurrentValue = regex;
|
||||
}
|
||||
else
|
||||
{
|
||||
db.DbSeasonInfo.Add(new DbSeasonInfo(id, mode, AnalyzerAction.Default, regex: regex));
|
||||
}
|
||||
}
|
||||
|
||||
await db.SaveChangesAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
internal AnalyzerAction GetAnalyzerAction(Guid id, AnalysisMode mode)
|
||||
{
|
||||
using var db = new IntroSkipperDbContext(_dbPath);
|
||||
|
@ -48,7 +48,7 @@ https://manifest.intro-skipper.org/manifest.json
|
||||
- #### [Scheduled tasks fail instantly](https://github.com/intro-skipper/intro-skipper/wiki/Troubleshooting#scheduled-tasks-fail-instantly)
|
||||
- #### [Plugin settings not saved](https://github.com/intro-skipper/intro-skipper/wiki/Troubleshooting#plugin-settings-not-saved)
|
||||
- #### [Skip button is not visible](https://github.com/intro-skipper/intro-skipper/wiki/Troubleshooting#skip-button-is-not-visible)
|
||||
- #### [Auto skip is not working](https://github.com/intro-skipper/intro-skipper/wiki/Troubleshooting#auto-skip-is-not-working)
|
||||
- #### [Autoskip is not working](https://github.com/intro-skipper/intro-skipper/wiki/Troubleshooting#autoskip-is-not-working)
|
||||
|
||||
## [API Documentation](https://github.com/intro-skipper/intro-skipper/blob/master/docs/api.md)
|
||||
|
||||
|
@ -15,6 +15,22 @@
|
||||
"sourceUrl": "https://github.com/intro-skipper/intro-skipper/releases/download/10.10/v1.10.10.11/intro-skipper-v1.10.10.11.zip",
|
||||
"checksum": "30a71fd3996e0fbe9076371539b1ca56",
|
||||
"timestamp": "2024-11-25T17:07:33Z"
|
||||
},
|
||||
{
|
||||
"version": "1.10.10.10",
|
||||
"changelog": "- See the full changelog at [GitHub](https://github.com/intro-skipper/intro-skipper/releases/tag/10.10/v1.10.10.10)\n",
|
||||
"targetAbi": "10.10.3.0",
|
||||
"sourceUrl": "https://github.com/intro-skipper/intro-skipper/releases/download/10.10/v1.10.10.10/intro-skipper-v1.10.10.10.zip",
|
||||
"checksum": "96ec29871e726ece01fbf7ccb24ae392",
|
||||
"timestamp": "2024-11-24T18:33:26Z"
|
||||
},
|
||||
{
|
||||
"version": "1.10.10.9",
|
||||
"changelog": "- See the full changelog at [GitHub](https://github.com/intro-skipper/intro-skipper/releases/tag/10.10/v1.10.10.9)\n",
|
||||
"targetAbi": "10.10.2.0",
|
||||
"sourceUrl": "https://github.com/intro-skipper/intro-skipper/releases/download/10.10/v1.10.10.9/intro-skipper-v1.10.10.9.zip",
|
||||
"checksum": "a39436ee4476a4e74acbafbaf1008dfb",
|
||||
"timestamp": "2024-11-17T21:14:18Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user