Update video details to fetch real title & thumbnail only

Refactored backend to extract actual YouTube title and thumbnail from HTML, removing simulated metadata fields (description, author, duration, views, upload date, tags). Simplified UI to display only title and thumbnail, and enhanced visuals with animated musical notes background. Removed related formatting and display logic for unused metadata.
This commit is contained in:
Michal Vrba
2025-12-11 16:26:43 +01:00
parent e5d2c104ac
commit 3a5de85cd4
3 changed files with 166 additions and 252 deletions
+41 -40
View File
@@ -1,5 +1,8 @@
using TuneRipper.Shared.Models;
using System.Text.RegularExpressions;
using System.Net.Http;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
namespace TuneRipper.Api.Services;
@@ -13,10 +16,12 @@ public interface IVideoService
public class VideoService : IVideoService
{
private readonly ILogger<VideoService> _logger;
private readonly HttpClient _httpClient;
public VideoService(ILogger<VideoService> logger)
{
_logger = logger;
_httpClient = new HttpClient();
}
public async Task<YouTubeVideoDetails?> GetVideoDetailsAsync(string url)
@@ -29,26 +34,48 @@ public class VideoService : IVideoService
return null;
}
// Simulate API call delay
await Task.Delay(Random.Shared.Next(500, 2000));
var videoId = ExtractVideoId(url);
// In production, use YouTube API or yt-dlp to get actual video details
var thumbnailUrl = $"https://img.youtube.com/vi/{videoId}/maxresdefault.jpg";
string? title = null;
// Fetch HTML and parse title
try
{
var html = await _httpClient.GetStringAsync(url);
// Try OpenGraph title first
var ogTitleMatch = Regex.Match(html, "<meta property='og:title' content='(.*?)'", RegexOptions.IgnoreCase);
if (!ogTitleMatch.Success)
{
ogTitleMatch = Regex.Match(html, "<meta property=\"og:title\" content=\"(.*?)\"", RegexOptions.IgnoreCase);
}
if (ogTitleMatch.Success)
{
title = ogTitleMatch.Groups[1].Value;
}
else
{
// Fallback to <title>
var titleMatch = Regex.Match(html, "<title>(.*?)</title>", RegexOptions.IgnoreCase);
if (titleMatch.Success)
{
title = titleMatch.Groups[1].Value.Replace(" - YouTube", "").Trim();
}
}
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to fetch or parse video title for {Url}", url);
title = $"YouTube Video {videoId}";
}
var videoDetails = new YouTubeVideoDetails
{
Id = videoId,
Title = GenerateSampleTitle(videoId),
Description = $"Video description for {videoId}. In production, this would be fetched from YouTube API.",
ThumbnailUrl = $"https://img.youtube.com/vi/{videoId}/maxresdefault.jpg",
Duration = TimeSpan.FromMinutes(Random.Shared.Next(2, 10)).Add(TimeSpan.FromSeconds(Random.Shared.Next(0, 59))),
Author = $"Channel_{Random.Shared.Next(1, 1000)}",
ViewCount = Random.Shared.Next(100, 5000000),
UploadDate = DateTime.UtcNow.AddDays(-Random.Shared.Next(1, 1000)),
Tags = GenerateSampleTags()
Title = title,
ThumbnailUrl = thumbnailUrl
};
_logger.LogInformation("Successfully fetched details for video {VideoId}", videoId);
_logger.LogInformation("Fetched image and title for video {VideoId}", videoId);
return videoDetails;
}
catch (Exception ex)
@@ -81,7 +108,6 @@ public class VideoService : IVideoService
try
{
// Different YouTube URL formats
var patterns = new Dictionary<string, string>
{
{ @"youtu\.be/([^?&]+)", "$1" },
@@ -106,29 +132,4 @@ public class VideoService : IVideoService
return string.Empty;
}
private static string GenerateSampleTitle(string videoId)
{
var sampleTitles = new[]
{
"Amazing Music Video",
"Best Song Ever",
"Epic Guitar Solo",
"Relaxing Piano Music",
"Dance Hit 2024",
"Acoustic Session",
"Live Performance",
"Official Music Video"
};
var randomTitle = sampleTitles[Random.Shared.Next(sampleTitles.Length)];
return $"{randomTitle} - {videoId}";
}
private static List<string> GenerateSampleTags()
{
var allTags = new[] { "music", "video", "song", "artist", "album", "live", "official", "acoustic", "cover", "remix" };
var tagCount = Random.Shared.Next(3, 7);
return allTags.OrderBy(x => Random.Shared.Next()).Take(tagCount).ToList();
}
}