242 lines
7.3 KiB
C#
242 lines
7.3 KiB
C#
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 });
|
|
}
|
|
}
|