forked from rrhett/EdlToMediaSegments
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathEdlToMediaSegmentsProvider.cs
More file actions
90 lines (77 loc) · 3.54 KB
/
EdlToMediaSegmentsProvider.cs
File metadata and controls
90 lines (77 loc) · 3.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
using Jellyfin.Database.Implementations.Enums;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.MediaSegments;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model;
using MediaBrowser.Model.MediaSegments;
using Microsoft.Extensions.Logging;
namespace EdlToMediaSegments;
/// <inheritdoc />
public class EdlToMediaSegmentsProvider(
IItemRepository itemRepository,
ILoggerFactory loggerFactory) : IMediaSegmentProvider
{
private readonly ILogger<EdlToMediaSegmentsProvider> logger = loggerFactory.CreateLogger<EdlToMediaSegmentsProvider>();
/// <inheritdoc />
public string Name => "EDL to Media Segments Provider";
/// <inheritdoc />
public ValueTask<bool> Supports(BaseItem item) => new(item is IHasMediaSources);
/// <inheritdoc />
public async Task<IReadOnlyList<MediaSegmentDto>> GetMediaSegments(MediaSegmentGenerationRequest request, CancellationToken cancellationToken)
{
var item = itemRepository.RetrieveItem(request.ItemId);
if (item is not IHasMediaSources mediaItem)
{
logger.LogDebug("Item {ItemId} is not IHasMediaSources", request.ItemId);
return [];
}
// Find EDL file next to the media file.
var edlFilePath = Path.ChangeExtension(mediaItem.Path, ".edl");
if (!await Task.Run(() => File.Exists(edlFilePath), cancellationToken))
{
logger.LogDebug("EDL file {EdlFilePath} does not exist for item {ItemId}", edlFilePath, request.ItemId);
return [];
}
logger.LogDebug("Found EDL file {EdlFilePath} for item {ItemId}", edlFilePath, request.ItemId);
// Read EDL file and parse the segments.
return ParseSegments(await Task.Run(() => File.ReadAllLines(edlFilePath), cancellationToken), item.Id, logger);
}
public static List<MediaSegmentDto> ParseSegments(string[] lines, Guid id, ILogger logger)
{
var segments = new List<MediaSegmentDto>();
char[] edlseparators = [' ', '\t'];
foreach (var line in lines)
{
var parts = line.Split(edlseparators, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length != 3)
{
logger.LogWarning("EDL line '{Line}' is not in the correct format", line);
continue;
}
if (!double.TryParse(parts[0], out var start) || !double.TryParse(parts[1], out var stop) || !int.TryParse(parts[2], out var action))
{
logger.LogWarning("EDL line '{Line}' has invalid format. Start='{Start}', Stop='{Stop}', Action='{Action}'", line, parts[0], parts[1], parts[2]);
continue;
}
segments.Add(new MediaSegmentDto
{
Id = Guid.NewGuid(),
ItemId = id,
Type = action switch
{
// This doesn't strictly agree with the EDL spec, but it is what Jellyfin understands.
0 => MediaSegmentType.Intro,
1 => MediaSegmentType.Preview,
2 => MediaSegmentType.Recap,
3 => MediaSegmentType.Commercial,
4 => MediaSegmentType.Outro,
_ => MediaSegmentType.Unknown
},
StartTicks = ConvertToTicks(start),
EndTicks = ConvertToTicks(stop)
});
}
return segments;
}
private static long ConvertToTicks(double seconds) => (long)(seconds * TimeSpan.TicksPerSecond);
}