diff --git a/tubearchivist/home/src/frontend/searching.py b/tubearchivist/home/src/frontend/searching.py index 9b4f993..9840cc3 100644 --- a/tubearchivist/home/src/frontend/searching.py +++ b/tubearchivist/home/src/frontend/searching.py @@ -136,6 +136,11 @@ class SearchHandler: date_str = datetime.strftime(date_refresh, "%d %b, %Y") hit["source"]["channel"]["channel_last_refresh"] = date_str + if "subtitle_fragment_id" in hit_keys: + youtube_id = hit["source"]["youtube_id"] + thumb_path = ThumbManager().vid_thumb_path(youtube_id) + hit["source"]["vid_thumb_url"] = f"/cache/{thumb_path}" + return hit @@ -159,6 +164,7 @@ class SearchForm: video_results = [] channel_results = [] playlist_results = [] + fulltext_results = [] if search_results: for result in search_results: if result["_index"] == "ta_video": @@ -167,11 +173,14 @@ class SearchForm: channel_results.append(result) elif result["_index"] == "ta_playlist": playlist_results.append(result) + elif result["_index"] == "ta_subtitle": + fulltext_results.append(result) all_results = { "video_results": video_results, "channel_results": channel_results, "playlist_results": playlist_results, + "fulltext_results": fulltext_results, } return all_results @@ -240,7 +249,7 @@ class SearchParser: "active": [], "subscribed": [], }, - "all": { + "full": { "index": "ta_subtitle", "term": [], }, @@ -303,11 +312,18 @@ class QueryBuilder: "video": self._build_video, "channel": self._build_channel, "playlist": self._build_playlist, + "full": self._build_fulltext, } build_must_list = exec_map[self.query_type] - query = {"size": 30, "query": {"bool": {"must": build_must_list()}}} + if self.query_type == "full": + query = build_must_list() + else: + query = { + "size": 30, + "query": {"bool": {"must": build_must_list()}}, + } return query @@ -448,3 +464,36 @@ class QueryBuilder: ) return must_list + + def _build_fulltext(self): + """build query for fulltext search""" + must_list = [] + + if (term := self.query_map.get("term")) is not None: + must_list.append( + { + "match": { + "subtitle_line": { + "query": term, + "fuzziness": "auto", + } + } + } + ) + + query = { + "size": 30, + "_source": {"excludes": "subtitle_line"}, + "query": {"bool": {"must": must_list}}, + "highlight": { + "fields": { + "subtitle_line": { + "number_of_fragments": 0, + "pre_tags": [''], + "post_tags": [""], + } + } + }, + } + + return query diff --git a/tubearchivist/home/templates/home/search.html b/tubearchivist/home/templates/home/search.html index a4b2620..7d90aa6 100644 --- a/tubearchivist/home/templates/home/search.html +++ b/tubearchivist/home/templates/home/search.html @@ -26,5 +26,11 @@

No playlists found.

+
+

Fulltext Results

+
+

No fulltext results found.

+
+
{% endblock content %} diff --git a/tubearchivist/static/css/style.css b/tubearchivist/static/css/style.css index 0b860ba..b14c8fc 100644 --- a/tubearchivist/static/css/style.css +++ b/tubearchivist/static/css/style.css @@ -781,6 +781,10 @@ button:hover { } /* multi search page */ +.multi-search-box { + padding-right: 20px; +} + .multi-search-box input { width: 100%; } diff --git a/tubearchivist/static/script.js b/tubearchivist/static/script.js index 6ec6212..d220229 100644 --- a/tubearchivist/static/script.js +++ b/tubearchivist/static/script.js @@ -869,6 +869,24 @@ function populateMultiSearchResults(allResults, queryType) { playlistBox.parentElement.style.display = "none"; } } + // fulltext + var allFullText = allResults.fulltext_results; + var fullTextBox = document.getElementById("fulltext-results"); + fullTextBox.innerHTML = ""; + fullTextBox.parentElement.style.display = "block"; + if (allFullText.length > 0) { + for (let i = 0; i < allFullText.length; i++) { + const fullText = allFullText[i]; + const fullTextDiv = createFulltext(fullText); + fullTextBox.appendChild(fullTextDiv); + } + } else { + if (queryType === "simple" || queryType == "full") { + fullTextBox.innerHTML = "

No fulltext items found.

"; + } else { + fullTextBox.parentElement.style.display = "none"; + } + } } @@ -992,6 +1010,40 @@ function createPlaylist(playlist, viewStyle) { return playlistDiv; } +function createFulltext(fullText) { + const videoId = fullText.source.youtube_id; + const videoTitle = fullText.source.title; + const thumbUrl = fullText.source.vid_thumb_url; + const channelId = fullText.source.subtitle_channel_id; + const channelName = fullText.source.subtitle_channel; + const subtitleLine = fullText.highlight.subtitle_line[0]; + const subtitle_start = fullText.source.subtitle_start.split(".")[0]; + const subtitle_end = fullText.source.subtitle_end.split(".")[0]; + const markup = ` + +
+
+ video-thumb +
+
+ play-icon +
+
+
+
+

${subtitle_start} - ${subtitle_end}

+

${subtitleLine}

+
+

${channelName}

+

${videoTitle}

+
+
+ ` + const fullTextDiv = document.createElement("div"); + fullTextDiv.setAttribute("class", "video-item list"); + fullTextDiv.innerHTML = markup; + return fullTextDiv +} // generic