Merge branch 'search'
This commit is contained in:
commit
e00e32dd81
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1,3 +1,5 @@
|
|||
.vscode/
|
||||
__pycache__/
|
||||
db.sqlite3
|
||||
videos/
|
||||
channel_archiver/yt-dlp-archive.txt
|
||||
|
|
|
|||
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"[python]": {
|
||||
"editor.defaultFormatter": "ms-python.python"
|
||||
},
|
||||
"python.formatting.provider": "none"
|
||||
}
|
||||
24
api/README.md
Normal file
24
api/README.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# VideoAPI
|
||||
|
||||
## Search
|
||||
`GET /api/?param=value`
|
||||
|
||||
### Parameters
|
||||
| Param | Value |
|
||||
|-|-|
|
||||
| `q` | The search query string. |
|
||||
| `limit` | A limit on the number of objects to be returned. Default is 6. |
|
||||
|
||||
### Response
|
||||
```
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Video 1"
|
||||
},
|
||||
{...}
|
||||
]
|
||||
```
|
||||
|
||||
### Examples
|
||||
> `GET /api/?q=foo&limit=3` will return the first 3 videos with "foo" in their name.
|
||||
0
api/__init__.py
Normal file
0
api/__init__.py
Normal file
7
api/urls.py
Normal file
7
api/urls.py
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
from django.urls import path, include
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path('search/', views.searchAPI),
|
||||
path('', include('sage_stream.api.urls')),
|
||||
]
|
||||
17
api/views.py
Normal file
17
api/views.py
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
from django.db.models import Q
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.decorators import api_view
|
||||
from core.models import Video
|
||||
from core.serializers import VideoSerializer
|
||||
|
||||
@api_view(['GET'])
|
||||
def searchAPI(request):
|
||||
q = request.GET.get('q', '')
|
||||
l = request.GET.get('limit', '6')
|
||||
try:
|
||||
l = int(l)
|
||||
videos = Video.objects.filter(Q(id__contains=q) | Q(name__contains=q))[:l]
|
||||
except:
|
||||
videos = Video.objects.filter(Q(id__contains=q) | Q(name__contains=q))
|
||||
serializer = VideoSerializer(videos, many=True)
|
||||
return Response(serializer.data)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,6 +1,4 @@
|
|||
from django.contrib import admin
|
||||
|
||||
|
||||
from .models import Video
|
||||
|
||||
@admin.register(Video)
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
7
core/serializers.py
Normal file
7
core/serializers.py
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
from rest_framework import serializers
|
||||
from .models import Video
|
||||
|
||||
class VideoSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Video
|
||||
fields = ['id', 'name']
|
||||
|
|
@ -12,8 +12,7 @@
|
|||
<link rel="stylesheet" href="{% static 'dist/css/adminlte.css' %}">
|
||||
<link rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,700&display=fallback">
|
||||
<link rel="stylesheet"
|
||||
href="{% static 'plugins/fontawesome-free/css/all.min.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'plugins/select2/css/select2.min.css' %}">
|
||||
</head>
|
||||
<body class="layout-top-nav" style="height: auto;">
|
||||
<div class="wrapper">
|
||||
|
|
@ -51,12 +50,18 @@
|
|||
</li>
|
||||
</ul>
|
||||
<div class="float-right" style="width:100%">
|
||||
|
||||
<form class="form-inline ml-0 ml-md-3 float-right">
|
||||
<div class="input-group input-group-sm">
|
||||
<input class="form-control form-control-navbar"
|
||||
<input class="form-control form-control-navbar" data-toggle="dropdown"
|
||||
type="search"
|
||||
placeholder="Search"
|
||||
aria-label="Search">
|
||||
aria-label="Search"
|
||||
autocomplete="off"
|
||||
id="search">
|
||||
<ul class="dropdown-menu" id="searchdropdown">
|
||||
<li><span class="dropdown-item-text">Nothing</span></li>
|
||||
</ul>
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-navbar" type="submit">
|
||||
<i class="fas fa-search"></i>
|
||||
|
|
@ -64,6 +69,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -123,5 +129,6 @@
|
|||
<script src="{% static 'plugins/jquery/jquery.min.js' %}"></script>
|
||||
<script src="{% static 'plugins/bootstrap/js/bootstrap.bundle.min.js' %}"></script>
|
||||
<script src="{% static 'dist/js/adminlte.min.js' %}"></script>
|
||||
<script src="{% static 'dist/js/searchsuggestions.js' %}"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
from django.urls import path
|
||||
from . import views
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path('', views.core, name='core'),
|
||||
path('status/', views.status, name='status'),
|
||||
path('about/', views.about, name='about'),
|
||||
path('random/', views.random, name='random'),
|
||||
path('view/<int:id>', views.view, name='view'),
|
||||
]
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
from django.shortcuts import render
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.http import HttpResponse
|
||||
from django.shortcuts import redirect
|
||||
from core.models import Video
|
||||
import random
|
||||
|
||||
# Create your views here.
|
||||
|
||||
|
|
@ -11,10 +8,9 @@ def core(request):
|
|||
videos = Video.objects.all()
|
||||
return render(request, 'base.html', {'videos': videos[::-1]})
|
||||
|
||||
|
||||
def random(request):
|
||||
videos = Video.objects.all().order_by("?")
|
||||
ran = videos.first();
|
||||
ran = videos.first()
|
||||
return redirect('/view/'+str(ran.id))
|
||||
|
||||
def status(request):
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -22,9 +22,7 @@ urlpatterns = [
|
|||
path('admin/', admin.site.urls),
|
||||
path('auth/', include('django.contrib.auth.urls')),
|
||||
path('', include('core.urls')),
|
||||
path('/', include('core.urls')),
|
||||
path('status/', include('core.urls')),
|
||||
path('api/', include('sage_stream.api.urls')),
|
||||
path('api/', include('api.urls')),
|
||||
]
|
||||
|
||||
if settings.DEBUG:
|
||||
|
|
|
|||
38
static/dist/js/searchsuggestions.js
vendored
Normal file
38
static/dist/js/searchsuggestions.js
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
const searchinput = document.querySelector("#search");
|
||||
const searchsuggestions = document.querySelector("#searchdropdown");
|
||||
const li = (c) => document.createElement("li").appendChild(c);
|
||||
searchinput.addEventListener("input", (e) => {
|
||||
q(e.target.value).then((d) => {
|
||||
if (d.length < 1) searchsuggestions.replaceChildren(nothing());
|
||||
else searchsuggestions.replaceChildren(...suggestions(d));
|
||||
})
|
||||
});
|
||||
async function q(s) {
|
||||
if (s.length > 0) {
|
||||
try {
|
||||
const response = await fetch("/api/search/?q=" + s);
|
||||
const result = await response.json();
|
||||
return result;
|
||||
} catch(error) {
|
||||
console.log("Error: " + error);
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
function nothing() {
|
||||
const span = document.createElement("span");
|
||||
span.setAttribute("class", "dropdown-item-text");
|
||||
span.insertAdjacentText("afterbegin", "Nothing");
|
||||
return li(span);
|
||||
}
|
||||
function suggestions(d) {
|
||||
const items = [];
|
||||
for (const vid of d) {
|
||||
const a = document.createElement("a");
|
||||
a.setAttribute("class", "dropdown-item")
|
||||
a.setAttribute("href", "/view/" + vid.id);
|
||||
a.insertAdjacentText("afterbegin", vid.name + " " + vid.id);
|
||||
items.push(li(a));
|
||||
}
|
||||
return items;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user