1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
|
module Invidious::Search
class Query
enum Type
# Types related to YouTube
Regular # Youtube search page
Channel # Youtube channel search box
# Types specific to Invidious
Subscriptions # Search user subscriptions
Playlist # "Add playlist item" search
end
getter type : Type = Type::Regular
@raw_query : String
@query : String = ""
property filters : Filters = Filters.new
property page : Int32
property region : String?
property channel : String = ""
# Return true if @raw_query is either `nil` or empty
private def empty_raw_query?
return @raw_query.empty?
end
# Same as `empty_raw_query?`, but named for external use
def empty?
return self.empty_raw_query?
end
# Getter for the query string.
# It is named `text` to reduce confusion (`search_query.text` makes more
# sense than `search_query.query`)
def text
return @query
end
# Initialize a new search query.
# Parameters are used to get the query string, the page number
# and the search filters (if any). Type tells this function
# where it is being called from (See `Type` above).
def initialize(
params : HTTP::Params,
@type : Type = Type::Regular,
@region : String? = nil
)
# Get the raw search query string (common to all search types). In
# Regular search mode, also look for the `search_query` URL parameter
if @type.regular?
@raw_query = params["q"]? || params["search_query"]? || ""
else
@raw_query = params["q"]? || ""
end
# Get the page number (also common to all search types)
@page = params["page"]?.try &.to_i? || 1
# Stop here is raw query in empty
# NOTE: maybe raise in the future?
return if self.empty_raw_query?
# Specific handling
case @type
when .channel?
# In "channel search" mode, filters are ignored, but we still parse
# the query prevent transmission of legacy filters to youtube.
#
_, _, @query, _ = Filters.from_legacy_filters(@raw_query)
#
when .playlist?
# In "add playlist item" mode, filters are parsed from the query
# string itself (legacy), and the channel is ignored.
#
@filters, _, @query, _ = Filters.from_legacy_filters(@raw_query)
#
when .subscriptions?, .regular?
if params["sp"]?
# Parse the `sp` URL parameter (youtube compatibility)
@filters = Filters.from_yt_params(params)
@query = @raw_query || ""
else
# Parse invidious URL parameters (sort, date, etc...)
@filters = Filters.from_iv_params(params)
@channel = params["channel"]? || ""
if @filters.default? && @raw_query.includes?(':')
# Parse legacy filters from query
@filters, @channel, @query, subs = Filters.from_legacy_filters(@raw_query)
else
@query = @raw_query || ""
end
if !@channel.empty?
# Switch to channel search mode (filters will be ignored)
@type = Type::Channel
elsif subs
# Switch to subscriptions search mode
@type = Type::Subscriptions
end
end
end
end
# Run the search query using the corresponding search processor.
# Returns either the results or an empty array of `SearchItem`.
def process(user : Invidious::User? = nil) : Array(SearchItem) | Array(ChannelVideo)
items = [] of SearchItem
# Don't bother going further if search query is empty
return items if self.empty_raw_query?
case @type
when .regular?, .playlist?
items = unnest_items(Processors.regular(self))
#
when .channel?
items = Processors.channel(self)
#
when .subscriptions?
if user
items = Processors.subscriptions(self, user.as(Invidious::User))
end
end
return items
end
# TODO: clean code
private def unnest_items(all_items) : Array(SearchItem)
items = [] of SearchItem
# Light processing to flatten search results out of Categories.
# They should ideally be supported in the future.
all_items.each do |i|
if i.is_a? Category
i.contents.each do |nest_i|
if !nest_i.is_a? Video
items << nest_i
end
end
else
items << i
end
end
return items
end
end
end
|