move project

This commit is contained in:
2025-07-27 00:33:05 +02:00
parent 4544be2999
commit 5f8fb01a9f
100 changed files with 651 additions and 406 deletions

View File

@@ -0,0 +1,241 @@
using System.Diagnostics;
using System.Threading.Tasks;
using Db.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using TagVid.Helpers;
using TagVid.Models;
namespace TagVid.Controllers;
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly AdapterContext _adapterContext;
public HomeController(ILogger<HomeController> logger, AdapterContext adapterContext)
{
_logger = logger;
_adapterContext = adapterContext;
}
public async Task<IActionResult> Index()
{
// check if the video path was set
var pathSettings = await _adapterContext.GetSettingAsync<string>(SettingName.VideoPath);
if (string.IsNullOrEmpty(pathSettings))
{
_logger.LogWarning("Video path is not set. Please configure the video path in settings.");
return RedirectToAction("Settings", "Home");
}
return View();
}
[HttpGet]
[Route("Home/Index/{id}")]
public async Task<IActionResult> Index(string id)
{
throw new NotImplementedException();
}
public async Task<IActionResult> Settings()
{
var vm = new SettingsViewModel
{
VideoPath = await _adapterContext.GetSettingAsync<string>(SettingName.VideoPath)
};
return View(vm);
}
[HttpPost]
public async Task<IActionResult> Settings(SettingsViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
// Validate the video path
if (string.IsNullOrWhiteSpace(model.VideoPath))
{
ModelState.AddModelError(nameof(model.VideoPath), "Video path cannot be empty.");
return View(model);
}
// Check if the path exists
if (!Directory.Exists(model.VideoPath))
{
ModelState.AddModelError(nameof(model.VideoPath), "The specified video path does not exist.");
return View(model);
}
// Check if the path is a directory
if (!System.IO.File.GetAttributes(model.VideoPath).HasFlag(FileAttributes.Directory))
{
ModelState.AddModelError(nameof(model.VideoPath), "The specified path must be a directory.");
return View(model);
}
// Save settings to the database
await _adapterContext.SetSettingAsync(SettingName.VideoPath, model.VideoPath);
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Upload(IFormFile videoFile)
{
_logger.LogInformation("Upload called with file: {FileName}", videoFile?.Name);
if (videoFile == null || videoFile.Length == 0)
{
return RedirectToAction("Index");
}
// Validate the video file
// no size limit for now, but could be added later
if (!videoFile.ContentType.StartsWith("video/"))
{
return RedirectToAction("Index");
}
// Save the video file to the configured path
var videoPath = await _adapterContext.GetSettingAsync<string>(SettingName.VideoPath);
var ext = Path.GetExtension(videoFile.FileName);
if (string.IsNullOrEmpty(videoPath) || !Directory.Exists(videoPath))
{
return RedirectToAction("Settings");
}
// asp.net docu suggests not using .FileName due to security reasons
var id = Guid.NewGuid().ToString();
var filePath = Path.Combine(videoPath, $"{id}{ext}");
using (var stream = new FileStream(filePath, FileMode.Create))
{
await videoFile.CopyToAsync(stream);
}
var data = await new FFProbeHelper<HomeController>(_logger).GetVideoMetadataAsync(filePath);
var video = new Video
{
Id = id,
Duration = data?.Duration ?? TimeSpan.Zero,
Width = data?.Width ?? 0,
Height = data?.Height ?? 0,
Extension = ext,
VideoTags = []
};
_adapterContext.Videos.Add(video);
await _adapterContext.SaveChangesAsync();
// redirect to the video details page
return RedirectToAction("Details", "Home", new { id = video.Id });
}
public async Task<IActionResult> Details(string id)
{
if (string.IsNullOrEmpty(id))
{
return NotFound();
}
var video = await _adapterContext.Videos
.Include(v => v.VideoTags)
.ThenInclude(vt => vt.Tag)
.FirstOrDefaultAsync(v => v.Id == id);
if (video == null)
{
return NotFound();
}
var vm = new DetailsViewModel
{
VideoId = id,
Title = video?.Title ?? string.Empty,
Tags = video?.VideoTags.Select(vt => vt.Tag.Name).ToList() ?? []
};
return View(vm);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Details(DetailsViewModel videoModel)
{
if (string.IsNullOrEmpty(videoModel.VideoId))
{
return NotFound();
}
var existingVideo = await _adapterContext.Videos
.Include(v => v.VideoTags)
.ThenInclude(vt => vt.Tag)
.FirstOrDefaultAsync(v => v.Id == videoModel.VideoId);
if (existingVideo == null)
{
return NotFound();
}
// Update video title
existingVideo.Title = videoModel.Title;
// Update tags
existingVideo.VideoTags.Clear();
foreach (var tagName in videoModel.Tags)
{
var tag = await _adapterContext.Tags.FirstOrDefaultAsync(t => t.Name == tagName);
if (tag == null)
{
tag = new Tag { Name = tagName };
_adapterContext.Tags.Add(tag);
}
existingVideo.VideoTags.Add(new VideoTag { Video = existingVideo, Tag = tag });
}
_adapterContext.Videos.Update(existingVideo);
await _adapterContext.SaveChangesAsync();
return RedirectToAction("Details", new { id = videoModel.VideoId });
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Delete(string id)
{
if (string.IsNullOrEmpty(id))
{
return NotFound();
}
var video = await _adapterContext.Videos.FindAsync(id);
if (video == null)
{
return NotFound();
}
// Delete the video file from the file system
var videoPath = await _adapterContext.GetSettingAsync<string>(SettingName.VideoPath);
if (!string.IsNullOrEmpty(videoPath))
{
var filePath = Path.Combine(videoPath, $"{video.Id}{video.Extension}");
if (System.IO.File.Exists(filePath))
{
System.IO.File.Delete(filePath);
}
}
// Remove the video from the database
_adapterContext.Videos.Remove(video);
await _adapterContext.SaveChangesAsync();
return RedirectToAction("Index");
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}