Implement playlists deletion
This commit is contained in:
@@ -7,8 +7,8 @@ using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using J2N.Collections.Generic.Extensions;
|
||||
using Jellyfin.Data.Entities;
|
||||
using Jellyfin.Data.Enums;
|
||||
using Jellyfin.Database.Implementations.Entities;
|
||||
using Jellyfin.Extensions;
|
||||
using Jellyfin.Plugin.TubeArchivistMetadata.TubeArchivist;
|
||||
using Jellyfin.Plugin.TubeArchivistMetadata.Utilities;
|
||||
@@ -29,9 +29,6 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Tasks
|
||||
/// </summary>
|
||||
public class JFToTubeArchivistPlaylistsSyncTask : IScheduledTask
|
||||
{
|
||||
private const string TAPlaylistIdRegex = @"^(.*)\((.*)\)$";
|
||||
private const string YTTAPlaylistNameFormatRegex = @"^(.*)\s\-\s(.*)\s\((.*)\)$";
|
||||
private const string TAPlaylistNameFormatRegex = @"^(.*)\s\((.*)\)$";
|
||||
private readonly ILogger<Plugin> _logger;
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
private readonly IUserManager _userManager;
|
||||
@@ -69,7 +66,7 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Tasks
|
||||
var items = new Dictionary<Guid, List<BaseItem>>();
|
||||
foreach (var playlist in playlists)
|
||||
{
|
||||
items[playlist.Id] = playlist.GetChildren(user, true, new InternalItemsQuery());
|
||||
items[playlist.Id] = new List<BaseItem>(playlist.GetChildren(user, true, new InternalItemsQuery()));
|
||||
}
|
||||
|
||||
return items;
|
||||
@@ -86,26 +83,6 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Tasks
|
||||
return totalVideosCount;
|
||||
}
|
||||
|
||||
private string? GetTAPlaylistIdFromName(string playlistName)
|
||||
{
|
||||
var regex = new Regex(TAPlaylistIdRegex);
|
||||
return regex.Match(playlistName).Groups[2].ToString();
|
||||
}
|
||||
|
||||
private string? GetTAPlaylistNameFromName(string playlistName)
|
||||
{
|
||||
var ytRegex = new Regex(YTTAPlaylistNameFormatRegex);
|
||||
var regex = new Regex(TAPlaylistNameFormatRegex);
|
||||
|
||||
var name = ytRegex.Match(playlistName).Groups[1].ToString();
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
name = regex.Match(playlistName).Groups[1].ToString();
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
private string GetPlaylistUpdatedName(string playlistName, string newId)
|
||||
{
|
||||
var index = playlistName.LastIndexOf(" (", StringComparison.CurrentCulture);
|
||||
@@ -153,10 +130,21 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Tasks
|
||||
var processedVideosCount = 0;
|
||||
|
||||
_logger.LogInformation("Found a total of {PlaylistsCount} playlists to analyze with a total of {VideosCount} videos", userPlaylists.Count, totalVideosCount);
|
||||
// TODO: Implement playlist deletion based on a configuration switch
|
||||
if (taPlaylists != null && true)
|
||||
{
|
||||
var taPlaylistsToDelete = taPlaylists.Where(tp => !userPlaylists.Select(up => Utils.GetTAPlaylistIdFromName(up.Name)).Contains(tp.Id));
|
||||
foreach (var taPlaylistToDelete in taPlaylistsToDelete)
|
||||
{
|
||||
_logger.LogInformation("Deleting TubeArchivist playlist {PlaylistName} ({PlaylistId})", taPlaylistToDelete.Name, taPlaylistToDelete.Id);
|
||||
await taApi.DeletePlaylist(taPlaylistToDelete.Id).ConfigureAwait(true);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var jfPlaylist in userPlaylists)
|
||||
{
|
||||
_logger.LogInformation("Analyzing playlist {PlaylistName}...", jfPlaylist.Name);
|
||||
var taPlaylistId = GetTAPlaylistIdFromName(jfPlaylist.Name);
|
||||
var taPlaylistId = Utils.GetTAPlaylistIdFromName(jfPlaylist.Name);
|
||||
if (taPlaylistId == null)
|
||||
{
|
||||
_logger.LogDebug("The playlist {PlaylistName} was not a TA playlist: could not find TA playlist id at the end", jfPlaylist.Name);
|
||||
@@ -193,6 +181,7 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Tasks
|
||||
_logger.LogDebug("Video {VideoName} from playlist {PlaylistName} not found in TA playlist", jfItems[i].Name, jfPlaylist.Name);
|
||||
var positionsToMove = i - position;
|
||||
action = new PlaylistItemAction(i, jfItems[i].ProviderIds[Constants.ProviderName], CustomPlaylistAction.Create, positionsToMove, jfItems[i].Name);
|
||||
taPlaylist.Entries.Insert(i, new PlaylistEntry(jfItems[i].ProviderIds[Constants.ProviderName], jfItems[i].Name, jfItems[i].Studios[0], i, true));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -210,17 +199,28 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Tasks
|
||||
var positionsToMove = i - position;
|
||||
action = new PlaylistItemAction(i, jfItems[i].ProviderIds[Constants.ProviderName], positionsToMove > 0 ? CustomPlaylistAction.Up : CustomPlaylistAction.Down, Math.Abs(positionsToMove), jfItems[i].Name);
|
||||
}
|
||||
|
||||
var temp = taPlaylist.Entries[position];
|
||||
taPlaylist.Entries.RemoveAt(position);
|
||||
taPlaylist.Entries.Insert(i, temp);
|
||||
}
|
||||
|
||||
itemsToProcess.Add(action);
|
||||
}
|
||||
|
||||
// Add videos to delete from the TA playlist
|
||||
itemsToProcess.AddRange(taPlaylist.Entries
|
||||
var itemsToDelete = taPlaylist.Entries
|
||||
.Where(e => !itemsToProcess
|
||||
.Select(i => i.YoutubeId)
|
||||
.Contains(e.YoutubeId))
|
||||
.Select(e => new PlaylistItemAction(e.YoutubeId, e.Title)));
|
||||
.ToList();
|
||||
foreach (var item in itemsToDelete)
|
||||
{
|
||||
_logger.LogDebug("Video {VideoName} not found in JF playlist, marking for deletion", item.Title);
|
||||
var ret = taPlaylist.Entries.Remove(item);
|
||||
}
|
||||
|
||||
itemsToProcess.AddRange(itemsToDelete.Select(e => new PlaylistItemAction(e.YoutubeId, e.Title)));
|
||||
|
||||
foreach (var item in itemsToProcess)
|
||||
{
|
||||
@@ -268,7 +268,7 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Tasks
|
||||
// If it doesn't, create a new custom playlist and add videos
|
||||
_logger.LogInformation("Playlist {PlaylistName} was not found on TubeArchivist. Creating a new playlist...", jfPlaylist.Name);
|
||||
|
||||
var playlistName = GetTAPlaylistNameFromName(jfPlaylist.Name);
|
||||
var playlistName = Utils.GetTAPlaylistNameFromName(jfPlaylist.Name);
|
||||
if (string.IsNullOrEmpty(playlistName))
|
||||
{
|
||||
playlistName = jfPlaylist.Name;
|
||||
@@ -379,7 +379,7 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Tasks
|
||||
[
|
||||
new TaskTriggerInfo
|
||||
{
|
||||
Type = TaskTriggerInfo.TriggerInterval,
|
||||
Type = TaskTriggerInfoType.IntervalTrigger,
|
||||
IntervalTicks = TimeSpan.FromSeconds(Plugin.Instance!.Configuration.TAJFTaskInterval).Ticks
|
||||
},
|
||||
];
|
||||
|
||||
@@ -91,7 +91,18 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Tasks
|
||||
continue;
|
||||
}
|
||||
|
||||
var userPlaylists = _playlistManager.GetPlaylists(user.Id);
|
||||
var userPlaylists = _playlistManager.GetPlaylists(user.Id).ToList();
|
||||
|
||||
// TODO: Implement playlist deletion based on a configuration switch
|
||||
if (true)
|
||||
{
|
||||
var jfPlaylistsToDelete = userPlaylists.Where(up => !taPlaylists.Select(tp => tp.Id).Contains(Utils.GetTAPlaylistIdFromName(up.Name)));
|
||||
foreach (var jfPlaylistToDelete in jfPlaylistsToDelete)
|
||||
{
|
||||
_logger.LogInformation("Deleting Jellyfin playlist {PlaylistName}", jfPlaylistToDelete.Name);
|
||||
_libraryManager.DeleteItem(jfPlaylistToDelete, new DeleteOptions());
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var taPlaylist in taPlaylists)
|
||||
{
|
||||
@@ -110,7 +121,7 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Tasks
|
||||
continue;
|
||||
}
|
||||
|
||||
var jfEntry = _libraryManager.GetItemList(new InternalItemsQuery
|
||||
var entries = _libraryManager.GetItemList(new InternalItemsQuery
|
||||
{
|
||||
HasAnyProviderId = new Dictionary<string, string>()
|
||||
{
|
||||
@@ -118,7 +129,9 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Tasks
|
||||
Constants.ProviderName, taEntry.YoutubeId
|
||||
}
|
||||
}
|
||||
}).FirstOrDefault();
|
||||
});
|
||||
|
||||
var jfEntry = entries.Count > 0 ? entries[0] : null;
|
||||
|
||||
if (jfEntry != null)
|
||||
{
|
||||
@@ -180,7 +193,7 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Tasks
|
||||
[
|
||||
new TaskTriggerInfo
|
||||
{
|
||||
Type = TaskTriggerInfo.TriggerInterval,
|
||||
Type = TaskTriggerInfoType.IntervalTrigger,
|
||||
IntervalTicks = TimeSpan.FromSeconds(Plugin.Instance!.Configuration.TAJFTaskInterval).Ticks
|
||||
},
|
||||
];
|
||||
|
||||
@@ -276,5 +276,20 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.TubeArchivist
|
||||
|
||||
return response.StatusCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a playlist.
|
||||
/// </summary>
|
||||
/// <param name="playlistId">Playlist id.</param>
|
||||
/// <returns>Whether the playlist has been deleted successfully or not.</returns>
|
||||
public async Task<bool> DeletePlaylist(string playlistId)
|
||||
{
|
||||
var deletePlaylistEndpoint = $"/api/playlist/";
|
||||
var url = new Uri(Utils.SanitizeUrl(Plugin.Instance!.Configuration.TubeArchivistUrl + deletePlaylistEndpoint + playlistId));
|
||||
var response = await client.DeleteAsync(url).ConfigureAwait(true);
|
||||
_logger.LogDebug("Response code: {Message}", response.StatusCode);
|
||||
|
||||
return response.StatusCode == HttpStatusCode.NoContent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,12 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Utilities
|
||||
/// </summary>
|
||||
public static class Utils
|
||||
{
|
||||
private const string TAPlaylistIdRegex = @"^(.*)\((.*)\)$";
|
||||
private const string YTTAPlaylistNameFormatRegex = @"^(.*)\s\-\s(.*)\s\((.*)\)$";
|
||||
private const string TAPlaylistNameFormatRegex = @"^(.*)\s\((.*)\)$";
|
||||
|
||||
/// <summary>
|
||||
/// Sanitize the given URL.
|
||||
/// Sanitizes the given URL.
|
||||
/// </summary>
|
||||
/// <param name="inputUrl">An URL string.</param>
|
||||
/// <returns>The URL string without spaces, doubled slashes and with a trailing slash.</returns>
|
||||
@@ -35,7 +39,7 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Utilities
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Format episodes and series descriptions replacing newlines with br tags.
|
||||
/// Formats episodes and series descriptions replacing newlines with br tags.
|
||||
/// </summary>
|
||||
/// <param name="description">String to format.</param>
|
||||
/// <returns>A string with \n replaced by br tags.</returns>
|
||||
@@ -57,7 +61,7 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Utilities
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get video name from file path on the disk.
|
||||
/// Gets video name from file path on the disk.
|
||||
/// </summary>
|
||||
/// <param name="path">File path on disk.</param>
|
||||
/// <returns>The video name.</returns>
|
||||
@@ -67,7 +71,7 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Utilities
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get channel name from directory path on the disk.
|
||||
/// Gets channel name from directory path on the disk.
|
||||
/// </summary>
|
||||
/// <param name="path">Directory path on disk.</param>
|
||||
/// <returns>The channel name.</returns>
|
||||
@@ -90,5 +94,35 @@ namespace Jellyfin.Plugin.TubeArchivistMetadata.Utilities
|
||||
return '/'; // Unix directory separator
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the TubeArchivist playlist id from Jellyfin playlist name.
|
||||
/// </summary>
|
||||
/// <param name="playlistName">The Jellyfin playlist name.</param>
|
||||
/// <returns>The TubeArchvist playlist id.</returns>
|
||||
public static string? GetTAPlaylistIdFromName(string playlistName)
|
||||
{
|
||||
var regex = new Regex(TAPlaylistIdRegex);
|
||||
return regex.Match(playlistName).Groups[2].ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the TubeArchivist playlist name from Jellyfin playlist name.
|
||||
/// </summary>
|
||||
/// <param name="playlistName">The Jellyfin playlist name.</param>
|
||||
/// <returns>The TubeArchivist playlist name.</returns>
|
||||
public static string? GetTAPlaylistNameFromName(string playlistName)
|
||||
{
|
||||
var ytRegex = new Regex(YTTAPlaylistNameFormatRegex);
|
||||
var regex = new Regex(TAPlaylistNameFormatRegex);
|
||||
|
||||
var name = ytRegex.Match(playlistName).Groups[1].ToString();
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
name = regex.Match(playlistName).Groups[1].ToString();
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user