Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@

Grayjay.Desktop.Installer/Result/*

# Local working notes
TODO.md

# User-specific files
*.rsuser
*.suo
Expand Down
82 changes: 82 additions & 0 deletions Grayjay.ClientServer/Controllers/BlockedChannelsController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using Grayjay.ClientServer.Models;
using Grayjay.ClientServer.States;
using Grayjay.Desktop.POC;
using Grayjay.Desktop.POC.Port.States;
using Microsoft.AspNetCore.Mvc;

namespace Grayjay.ClientServer.Controllers
{
[Route("[controller]/[action]")]
public class BlockedChannelsController : ControllerBase
{
public class BlockChannelRequest
{
public string Url { get; set; }
public string Name { get; set; }
public string Thumbnail { get; set; }
public string PluginId { get; set; }
}

[HttpGet]
public ActionResult<List<BlockedChannel>> List()
{
return Ok(StateBlockedChannels.Instance.GetBlocked());
}

[HttpGet]
public ActionResult<bool> IsBlocked(string url)
{
return Ok(StateBlockedChannels.Instance.IsBlocked(url));
}

[HttpPost]
public ActionResult<BlockedChannel> Add([FromBody] BlockChannelRequest request)
{
if (request == null || string.IsNullOrEmpty(request.Url))
throw new BadHttpRequestException("A channel url is required");

var blocked = new BlockedChannel()
{
Url = request.Url,
Name = request.Name,
Thumbnail = request.Thumbnail,
PluginId = request.PluginId,
BlockedTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
};

try
{
var client = StatePlatform.GetChannelClientOrNull(request.Url);
if (client != null)
{
var channel = StatePlatform.GetChannel(request.Url);
if (channel != null)
{
blocked.Url = channel.Url;
blocked.Name = channel.Name;
blocked.Thumbnail = channel.Thumbnail;
blocked.PluginId = client.Config.ID;
blocked.ChannelId = channel.ID?.Value;
blocked.UrlAlternatives = channel.UrlAlternatives?.ToList() ?? new List<string>();
}
}
}
catch (Exception ex)
{
Logger.w(nameof(BlockedChannelsController), $"Failed to resolve channel [{request.Url}], using provided data", ex);
}

StateBlockedChannels.Instance.Add(blocked, true);
return Ok(blocked);
}

[HttpGet]
public ActionResult<bool> Remove(string url)
{
if (string.IsNullOrEmpty(url))
throw new BadHttpRequestException("A channel url is required");
StateBlockedChannels.Instance.Remove(url, true);
return Ok(true);
}
}
}
24 changes: 22 additions & 2 deletions Grayjay.ClientServer/Controllers/DetailsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,18 @@ public static PlatformPostDetails EnsurePost(WindowState state)
=> state.DetailsState.PostLoaded ?? throw new BadHttpRequestException("No post loaded");
public static PlatformVideoDetails EnsureVideo(WindowState state)
=> state.DetailsState.VideoLoaded ?? throw new BadHttpRequestException("No video loaded");
private static void AssertNotBlocked(PlatformVideoDetails video)
{
if (video != null && StateBlockedChannels.Instance.IsBlocked(video.Author))
throw new DialogException(new ExceptionModel()
{
Type = ExceptionModel.EXCEPTION_GENERAL,
Title = "Channel blocked",
Message = $"The channel [{(string.IsNullOrEmpty(video.Author?.Name) ? video.Author?.Url : video.Author?.Name)}] is blocked, so this video cannot be played.",
CanRetry = false,
TypeName = nameof(DialogException)
});
}
public static VideoLocal EnsureLocal(WindowState state) => state.DetailsState.VideoLocal ?? throw new BadHttpRequestException("No offline video loaded");
private RefPager<PlatformComment> EnsureComments()
=> this.State().DetailsState.CommentPager ?? throw new BadHttpRequestException("No comments loaded");
Expand Down Expand Up @@ -342,6 +354,7 @@ public VideoLoadResult VideoLoad(string url)

if (contentDetails is PlatformVideoDetails video)
{
AssertNotBlocked(video);
ChangeVideo(video, local);
}
else if (local != null)
Expand Down Expand Up @@ -605,6 +618,7 @@ public List<Chapter> GetVideoChapters(string url)
public IActionResult Download(string url, int videoIndex, int audioIndex)
{
var video = EnsureVideo(this.State());
AssertNotBlocked(video);
var sourceVideo = (videoIndex >= 0) ? video.Video.VideoSources[videoIndex] : null;
var sourceAudio = (audioIndex >= 0 && video.Video is UnMuxedVideoDescriptor unmuxed) ? unmuxed.AudioSources[audioIndex] : null;

Expand All @@ -624,8 +638,10 @@ public IActionResult Download(string url, int videoIndex, int audioIndex)
[HttpGet]
public List<VideoQuality> VideoQualities(int videoIndex)
{
var video = (videoIndex == -999) ? EnsureVideo(this.State()).Live :
EnsureVideo(this.State()).Video.VideoSources[videoIndex];
var loaded = EnsureVideo(this.State());
AssertNotBlocked(loaded);
var video = (videoIndex == -999) ? loaded.Live :
loaded.Video.VideoSources[videoIndex];
if(video is HLSManifestSource hlsVideo)
{
var hlsResponse = _qualityClient.GET(hlsVideo.Url, new Engine.Models.HttpHeaders());
Expand Down Expand Up @@ -677,6 +693,7 @@ public static (IVideoSource? Video, IAudioSource? Audio, ISubtitleSource? Subtit
public async Task<IActionResult> SourceDash(int videoIndex, int audioIndex, int subtitleIndex, bool videoIsLocal = false, bool audioIsLocal = false, bool subtitleIsLocal = false, bool isLoopback = true, string? tag = null)
{
var state = this.State();
AssertNotBlocked(EnsureVideo(state));
try
{
(var taskGenerateSourceDash, var promiseMetadata) = GenerateSourceDash(state, videoIndex, audioIndex, subtitleIndex, videoIsLocal, audioIsLocal, subtitleIsLocal, new ProxySettings(isLoopback));
Expand Down Expand Up @@ -966,6 +983,7 @@ private static string getRequestExecutorProxy(string registerUrl, RequestExecuto
[HttpGet]
public async Task<IActionResult> SourceHLS(int videoIndex = -1, int audioIndex = -1, int subtitleIndex = -1, bool subtitleIsLocal = false, bool isLoopback = true, string? modifierId = null)
{
AssertNotBlocked(EnsureVideo(this.State()));
return Content(await GenerateSourceHLS(this.State(), videoIndex, audioIndex, subtitleIndex, subtitleIsLocal, new ProxySettings(isLoopback), modifierId), "application/x-mpegurl");
}

Expand Down Expand Up @@ -1133,6 +1151,7 @@ public async Task<IActionResult> SourceAuto()
else
{
var video = EnsureVideo(this.State());
AssertNotBlocked(video);
var bestVideoSourceIndex = VideoHelper.SelectBestVideoSourceIndex(video.Video.VideoSources.Cast<IVideoSource>().ToList(), GrayjaySettings.Instance.Playback.GetPreferredQualityPixelCount(), new List<string>() { "video/mp4" });
var bestAudioSourceIndex = (video.Video is UnMuxedVideoDescriptor unmuxed) ?
VideoHelper.SelectBestAudioSourceIndex(unmuxed.AudioSources.Cast<IAudioSource>().ToList(), new List<string>() { "audio/mp4" }, GrayjaySettings.Instance.Playback.GetPrimaryLanguage(), 9999 * 9999) :
Expand Down Expand Up @@ -1173,6 +1192,7 @@ public async Task<IActionResult> SourceProxy(int videoIndex, int audioIndex, int
public static async Task<SourceDescriptor> GenerateSourceProxy(WindowState state, int videoIndex, int audioIndex, int subtitleIndex, bool videoIsLocal = false, bool audioIsLocal = false, bool subtitleIsLocal = false, ProxySettings? proxySettings = null, string? tag = null, bool forceReady = false)
{
var video = EnsureVideo(state);
AssertNotBlocked(video);

if (videoIndex == -999)
{
Expand Down
40 changes: 40 additions & 0 deletions Grayjay.ClientServer/Models/BlockedChannel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System.Text.Json.Serialization;

namespace Grayjay.ClientServer.Models
{
public class BlockedChannel
{
[JsonPropertyName("url")]
public string Url { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("thumbnail")]
public string? Thumbnail { get; set; }
[JsonPropertyName("pluginId")]
public string? PluginId { get; set; }
[JsonPropertyName("blockedTime")]
public long BlockedTime { get; set; }
[JsonPropertyName("channelId")]
public string? ChannelId { get; set; }
[JsonPropertyName("urlAlternatives")]
public List<string> UrlAlternatives { get; set; } = new List<string>();

public bool IsSameUrl(string url)
{
if (string.IsNullOrEmpty(url))
return false;
return string.Equals(Url, url, StringComparison.OrdinalIgnoreCase) ||
UrlAlternatives.Any(x => string.Equals(x, url, StringComparison.OrdinalIgnoreCase));
}

public bool IsSameUrl(IEnumerable<string> urls)
{
if (urls == null)
return false;
var list = urls.Where(x => !string.IsNullOrEmpty(x)).Select(x => x.ToLower()).ToList();
if (list.Count == 0)
return false;
return list.Contains(Url.ToLower()) || UrlAlternatives.Any(a => list.Contains(a.ToLower()));
}
}
}
8 changes: 8 additions & 0 deletions Grayjay.ClientServer/States/StateBackup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public static List<IManagedStore> GetAllMigrationStores()
{
return StateSubscriptions.ToMigrateCheck()
.Concat(StatePlaylists.ToMigrateCheck())
.Concat(StateBlockedChannels.Instance.ToMigrateCheck())
.ToList();
}

Expand Down Expand Up @@ -366,6 +367,13 @@ public static ImportCache GetCache()

var allSubscriptions = StateSubscriptions.GetSubscriptions();
var channels = allSubscriptions.Select(x => x.Channel).ToList();
channels.AddRange(StateBlockedChannels.Instance.GetBlocked().Select(x => new PlatformChannel()
{
Url = x.Url,
Name = x.Name,
Thumbnail = x.Thumbnail,
UrlAlternatives = x.UrlAlternatives
}));
return new ImportCache()
{
Channels = channels,
Expand Down
Loading