From 3e36eea37fb1613c21a4f103cf4647eb6e2558a9 Mon Sep 17 00:00:00 2001 From: Kayra Uylar <52961639+kuylar@users.noreply.github.com> Date: Thu, 15 Aug 2024 09:03:07 +0300 Subject: [PATCH] Audio only video player (#165) * Implement audio only watch pages * Add quality selector labels for audio formats * Fix video posters * Add buttons in thumbnails for shortcuts * Make thumbnails clickable again --- LightTube/Contexts/EmbedContext.cs | 4 +- LightTube/Contexts/PlayerContext.cs | 19 ++++----- LightTube/Contexts/WatchContext.cs | 8 ++-- LightTube/Controllers/YoutubeController.cs | 12 +++--- LightTube/Resources/Localization/en.json | 5 +++ LightTube/Views/Shared/Player.cshtml | 32 ++++----------- .../Renderers/Channel/ChannelGrid.cshtml | 22 +++++++++- .../Renderers/Channel/ChannelHome.cshtml | 2 +- .../Shared/Renderers/ContainerRenderer.cshtml | 22 +++++++++- .../Shared/Renderers/SearchRenderer.cshtml | 22 +++++++++- LightTube/Views/Youtube/Watch.cshtml | 41 ++++++++++++++++++- LightTube/wwwroot/css/renderers.css | 36 ++++++++++++++++ 12 files changed, 170 insertions(+), 55 deletions(-) diff --git a/LightTube/Contexts/EmbedContext.cs b/LightTube/Contexts/EmbedContext.cs index 911bc4d..3caf194 100644 --- a/LightTube/Contexts/EmbedContext.cs +++ b/LightTube/Contexts/EmbedContext.cs @@ -9,10 +9,10 @@ public class EmbedContext : BaseContext public InnerTubeVideo Video; public EmbedContext(HttpContext context, InnerTubePlayer innerTubePlayer, InnerTubeVideo innerTubeNextResponse, - bool compatibility, SponsorBlockSegment[] sponsors) : base(context) + bool compatibility, SponsorBlockSegment[] sponsors, bool audioOnly) : base(context) { Player = new PlayerContext(context, innerTubePlayer, innerTubeNextResponse, "embed", compatibility, - context.Request.Query["q"], sponsors); + context.Request.Query["q"], sponsors, audioOnly); Video = innerTubeNextResponse; } diff --git a/LightTube/Contexts/PlayerContext.cs b/LightTube/Contexts/PlayerContext.cs index f851878..c202de4 100644 --- a/LightTube/Contexts/PlayerContext.cs +++ b/LightTube/Contexts/PlayerContext.cs @@ -1,8 +1,6 @@ -using InnerTube; -using InnerTube.Models; +using InnerTube.Models; using InnerTube.Protobuf; using InnerTube.Protobuf.Responses; -using InnerTube.Renderers; using Newtonsoft.Json; namespace LightTube.Contexts; @@ -14,15 +12,16 @@ public class PlayerContext : BaseContext public Exception? Exception; public bool UseHls; public bool UseDash; - public Thumbnail[] Thumbnails = []; + public Thumbnail[] Thumbnails; public string? ErrorMessage = null; public int PreferredItag = 18; public bool UseEmbedUi = false; public string? ClassName; public SponsorBlockSegment[] Sponsors; + public bool AudioOnly; public PlayerContext(HttpContext context, InnerTubePlayer innerTubePlayer, InnerTubeVideo? video, string className, - bool compatibility, string? preferredItag, SponsorBlockSegment[] sponsors) : base(context) + bool compatibility, string? preferredItag, SponsorBlockSegment[] sponsors, bool audioOnly) : base(context) { Player = innerTubePlayer; Video = video; @@ -31,6 +30,8 @@ public PlayerContext(HttpContext context, InnerTubePlayer innerTubePlayer, Inner Sponsors = sponsors; UseHls = !compatibility && !string.IsNullOrWhiteSpace(innerTubePlayer.HlsManifestUrl); // Prefer HLS UseDash = innerTubePlayer.AdaptiveFormats.Any() && !compatibility; + AudioOnly = audioOnly; + Thumbnails = innerTubePlayer.Details.Thumbnails; // Formats if (!Configuration.ProxyEnabled) { @@ -81,11 +82,9 @@ public PlayerContext(HttpContext context, Exception e) : base(context) public int? GetFirstItag() => GetPreferredFormat()?.Itag; public Format? GetPreferredFormat() => - Player?.Formats.FirstOrDefault(x => x.Itag == PreferredItag && x.Itag != 17) ?? - Player?.Formats.FirstOrDefault(x => x.Itag != 17); + AudioOnly + ? Player?.AdaptiveFormats.FirstOrDefault(x => x.Mime.StartsWith("audio/")) + : Player?.Formats.FirstOrDefault(); public string GetClass() => ClassName is not null ? $" {ClassName}" : ""; - - public IEnumerable GetFormatsInPreferredOrder() => - Player!.Formats.OrderBy(x => x.Itag != PreferredItag).Where(x => x.Itag != 17); } \ No newline at end of file diff --git a/LightTube/Contexts/WatchContext.cs b/LightTube/Contexts/WatchContext.cs index 9bcfe72..418d6bd 100644 --- a/LightTube/Contexts/WatchContext.cs +++ b/LightTube/Contexts/WatchContext.cs @@ -16,10 +16,10 @@ public class WatchContext : BaseContext public WatchContext(HttpContext context, InnerTubePlayer innerTubePlayer, InnerTubeVideo innerTubeVideo, ContinuationResponse? comments, bool compatibility, int dislikes, - SponsorBlockSegment[] sponsors) : base(context) + SponsorBlockSegment[] sponsors, bool audioOnly) : base(context) { Player = new PlayerContext(context, innerTubePlayer, innerTubeVideo, "embed", compatibility, - context.Request.Query["q"], sponsors); + context.Request.Query["q"], sponsors, audioOnly); Video = innerTubeVideo; Playlist = Video.Playlist; Comments = comments; @@ -80,10 +80,10 @@ public WatchContext(HttpContext context, Exception e, InnerTubeVideo innerTubeVi public WatchContext(HttpContext context, InnerTubePlayer innerTubePlayer, InnerTubeVideo innerTubeVideo, DatabasePlaylist? playlist, ContinuationResponse? comments, bool compatibility, int dislikes, - SponsorBlockSegment[] sponsors) : base(context) + SponsorBlockSegment[] sponsors, bool audioOnly) : base(context) { Player = new PlayerContext(context, innerTubePlayer, innerTubeVideo, "embed", compatibility, - context.Request.Query["q"], sponsors); + context.Request.Query["q"], sponsors, audioOnly); Video = innerTubeVideo; Playlist = playlist?.GetVideoPlaylistInfo(innerTubeVideo.Id, DatabaseManager.Users.GetUserFromId(playlist.Author).Result!, diff --git a/LightTube/Controllers/YoutubeController.cs b/LightTube/Controllers/YoutubeController.cs index 46f1a4b..4264587 100644 --- a/LightTube/Controllers/YoutubeController.cs +++ b/LightTube/Controllers/YoutubeController.cs @@ -16,7 +16,8 @@ namespace LightTube.Controllers; public class YoutubeController(SimpleInnerTubeClient innerTube, HttpClient client) : Controller { [Route("/embed/{v}")] - public async Task Embed(string v, bool contentCheckOk, bool compatibility = false) + public async Task Embed(string v, bool contentCheckOk, bool compatibility = false, + bool audioOnly = false) { InnerTubePlayer? player; Exception? e; @@ -49,11 +50,12 @@ public async Task Embed(string v, bool contentCheckOk, bool compa language: HttpContext.GetInnerTubeLanguage(), region: HttpContext.GetInnerTubeRegion()); if (player is null || e is not null) return View(new EmbedContext(HttpContext, e ?? new Exception("player is null"), video)); - return View(new EmbedContext(HttpContext, player, video, compatibility, sponsors)); + return View(new EmbedContext(HttpContext, player, video, compatibility, sponsors, audioOnly)); } [Route("/watch")] - public async Task Watch(string v, string? list, bool contentCheckOk, bool compatibility = false) + public async Task Watch(string v, string? list, bool contentCheckOk, bool compatibility = false, + bool audioOnly = false) { InnerTubePlayer? player; Exception? e; @@ -127,14 +129,14 @@ public async Task Watch(string v, string? list, bool contentCheck return View(new WatchContext(HttpContext, e ?? new Exception("player is null"), video, pl, comments, dislikes)); return View(new WatchContext(HttpContext, player, video, pl, comments, compatibility, dislikes, - sponsors)); + sponsors, audioOnly)); } if (player is null || e is not null) return View(new WatchContext(HttpContext, e ?? new Exception("player is null"), video, comments, dislikes)); return View( - new WatchContext(HttpContext, player, video, comments, compatibility, dislikes, sponsors)); + new WatchContext(HttpContext, player, video, comments, compatibility, dislikes, sponsors, audioOnly)); } [Route("/results")] diff --git a/LightTube/Resources/Localization/en.json b/LightTube/Resources/Localization/en.json index f0ec5fd..7aa8f4b 100644 --- a/LightTube/Resources/Localization/en.json +++ b/LightTube/Resources/Localization/en.json @@ -99,6 +99,8 @@ "pagination.first": "First Page", "pagination.next": "Next Page", + "player.audioQuality": "(Audio only) {0}kbps", + "playlist.add.title": "Add video to playlist", "playlist.add.body": "Please select the playlist to add this video into.", "playlist.add.confirm": "Add", @@ -245,6 +247,9 @@ "video.unavailable": "Unavailable", "video.trailer.title": "Trailer", "video.trailer.body": "Livestream will begin in {0}", + "video.watch": "Watch", + "video.listen": "Listen", + "video.download": "Download", "watch.like.unavailable": "Like", "watch.dislike.unavailable": "Dislike", diff --git a/LightTube/Views/Shared/Player.cshtml b/LightTube/Views/Shared/Player.cshtml index 2b6cb48..b58009e 100644 --- a/LightTube/Views/Shared/Player.cshtml +++ b/LightTube/Views/Shared/Player.cshtml @@ -75,24 +75,9 @@ } } -else if ((Model.UseHls || Model.UseDash) && !Model.Player.Formats.Any()) +else if ((Model.UseHls || Model.UseDash) && !Model.Player.Formats.Any() && !Model.AudioOnly) {