From 2709154ceed0b47eb47c90ef7646642b97a89bd1 Mon Sep 17 00:00:00 2001 From: hak8or Date: Sat, 3 Mar 2018 00:21:49 -0500 Subject: [PATCH] Added search ability --- YTManager/Controllers/Videos.cs | 100 +++++++++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) diff --git a/YTManager/Controllers/Videos.cs b/YTManager/Controllers/Videos.cs index fc226ae..512d626 100644 --- a/YTManager/Controllers/Videos.cs +++ b/YTManager/Controllers/Videos.cs @@ -1,6 +1,8 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using System.Xml; using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -75,6 +77,102 @@ namespace YTManager.Controllers { return Ok(converted); } + struct Search_Query + { + // Tags. + public List Tags; + + // What the duration type is. + public enum _Duration_Type { LessThan, GreaterThan, Unset }; + public _Duration_Type Duration_Type; + + // What the expected duration of the videos are. + public TimeSpan Duration; + + // Remaining tokens. + public List Remaining_Tokens; + } + + + // Searches for videos using the specified tag(s). + [HttpGet("search/{searchstr}")] + public async Task Search([FromRoute] string searchstr) { + // What to use for searching videos with. + var parsed_query = new Search_Query(); + + // Assume the search string is comma seperated. + parsed_query.Remaining_Tokens = searchstr.Split(',').Select(t => t.Trim().ToLower()).ToList(); + + // Find a potential single video duration. + List greaterthan = parsed_query.Remaining_Tokens.Where(token => token.StartsWith('>')).ToList(); + List lessthan = parsed_query.Remaining_Tokens.Where(token => token.StartsWith('<')).ToList(); + if (greaterthan.Count() + lessthan.Count() > 1) + return BadRequest(); + if (greaterthan.Count() == 1) parsed_query.Duration_Type = Search_Query._Duration_Type.GreaterThan; + else if (lessthan.Count() == 1) parsed_query.Duration_Type = Search_Query._Duration_Type.LessThan; + else parsed_query.Duration_Type = Search_Query._Duration_Type.Unset; + greaterthan.ForEach(g => parsed_query.Remaining_Tokens.Remove(g)); + lessthan.ForEach(l => parsed_query.Remaining_Tokens.Remove(l)); + + // Attempt to parse it using ISO8601 duration. + if (parsed_query.Duration_Type != Search_Query._Duration_Type.Unset) { + string timingstr = "PT" + greaterthan + .Concat(lessthan) + .Select(s => s.TrimStart(new char[] { '<', '>' })) + .First(); + + try { + parsed_query.Duration = XmlConvert.ToTimeSpan(timingstr.ToUpper()); + } catch (Exception) { + return BadRequest("Failed to parse duration."); + } + } + + // Find which tokens are explicit tags + parsed_query.Tags = await db.Tags + .Where(t => parsed_query.Remaining_Tokens.Any(token => token == t.Name.ToLower())) + .ToListAsync(); + parsed_query.Tags.ForEach(tag => parsed_query.Remaining_Tokens.Remove(tag.Name.ToLower())); + + // Get from the database all videos which satisfy the query via + // AND'ing all the queries. + var dbquery = db.Videos.Select(v => v); + + // Get all videos that satisfy the time duration. + if (parsed_query.Duration_Type == Search_Query._Duration_Type.GreaterThan) + dbquery = dbquery.Where(v => v.Duration >= parsed_query.Duration); + else if (parsed_query.Duration_Type == Search_Query._Duration_Type.LessThan) + dbquery = dbquery.Where(v => v.Duration <= parsed_query.Duration); + + // Match videos where the tag matches. + parsed_query.Tags.ForEach(tag => dbquery = dbquery.Where(V => V.Tags.Any(vt => vt.Name == tag.Name))); + + // Get all videos that match their title, description, or channel name + // with the remaining tokens. + parsed_query.Remaining_Tokens.ForEach(token => { + dbquery = dbquery.Where(v => + v.Channel.Title.ToLower().Contains(token) || + v.Title.ToLower().Contains(token) || + v.Description.ToLower().Contains(token)); + }); + + // Get all the relevant videos. + var vids = await dbquery + .OrderByDescending(i => i.AddedToYT) + .Take(max_per_query) + .Include(v => v.Channel) + .Include(v => v.Tags) + .ToListAsync(); + + // Convert them to what we will send out. + var converted = vids + .Select(v => new Video_ForAPI(v)) + .ToList(); + + // Convert all the videos to what we will send back. + return Ok(converted); + } + // Returns the most recent videos of a channel. [HttpGet("fromchannel/{channelName}")] public async Task Get_Channel_Videos([FromRoute] string channelName) {