Add search function
This commit is contained in:
parent
6a6334b912
commit
03905756ca
121
FAAPI/FAAPI.py
121
FAAPI/FAAPI.py
@ -1,4 +1,5 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from enum import Enum
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
@ -444,6 +445,64 @@ class User:
|
|||||||
return self.username
|
return self.username
|
||||||
|
|
||||||
|
|
||||||
|
class OrderByEnum(Enum):
|
||||||
|
relevancy = 'relevancy'
|
||||||
|
date = 'date'
|
||||||
|
popularity = 'popularity'
|
||||||
|
|
||||||
|
|
||||||
|
class OrderEnum(Enum):
|
||||||
|
ascending = 'asc'
|
||||||
|
descending = 'desc'
|
||||||
|
|
||||||
|
|
||||||
|
class RangeEnum(Enum):
|
||||||
|
day = 'day'
|
||||||
|
three_days = '3days'
|
||||||
|
week = 'week'
|
||||||
|
month = 'month'
|
||||||
|
all_time = 'all'
|
||||||
|
|
||||||
|
|
||||||
|
class MatchModeEnum(Enum):
|
||||||
|
all = 'all'
|
||||||
|
any = 'any'
|
||||||
|
extended = 'extended'
|
||||||
|
|
||||||
|
|
||||||
|
class SearchOpts:
|
||||||
|
"""
|
||||||
|
A container for FurAffinity search options.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, order_by: OrderByEnum = OrderByEnum.relevancy,
|
||||||
|
order_direction: OrderEnum = OrderEnum.descending,
|
||||||
|
range: RangeEnum = RangeEnum.all_time,
|
||||||
|
match_mode: MatchModeEnum = MatchModeEnum.extended,
|
||||||
|
rating_general: bool = True,
|
||||||
|
rating_mature: bool = True,
|
||||||
|
rating_adult: bool = True,
|
||||||
|
type_art: bool = True,
|
||||||
|
type_music: bool = True,
|
||||||
|
type_flash: bool = True,
|
||||||
|
type_story: bool = True,
|
||||||
|
type_photo: bool = True,
|
||||||
|
type_poetry: bool = True):
|
||||||
|
self.order_by: OrderByEnum = order_by
|
||||||
|
self.order_direction: OrderEnum = order_direction
|
||||||
|
self.range: RangeEnum = range
|
||||||
|
self.match_mode: MatchModeEnum = match_mode
|
||||||
|
self.rating_general: bool = rating_general
|
||||||
|
self.rating_mature: bool = rating_mature
|
||||||
|
self.rating_adult: bool = rating_adult
|
||||||
|
self.type_art: bool = type_art
|
||||||
|
self.type_music: bool = type_music
|
||||||
|
self.type_flash: bool = type_flash
|
||||||
|
self.type_story: bool = type_story
|
||||||
|
self.type_photo: bool = type_photo
|
||||||
|
self.type_poetry: bool = type_poetry
|
||||||
|
|
||||||
|
|
||||||
class FAAPI:
|
class FAAPI:
|
||||||
"""
|
"""
|
||||||
A basic FurAffinity API instance.
|
A basic FurAffinity API instance.
|
||||||
@ -567,6 +626,68 @@ class FAAPI:
|
|||||||
submission._api = self
|
submission._api = self
|
||||||
return submission
|
return submission
|
||||||
|
|
||||||
|
def search(self, search: str, results: int = 48, opts: SearchOpts = SearchOpts()) -> List[Submission]:
|
||||||
|
"""
|
||||||
|
Searches FurAffinity with the given string.
|
||||||
|
Results are
|
||||||
|
:param search: Search term
|
||||||
|
:param results: Number of results to return
|
||||||
|
:param opts: Search options
|
||||||
|
:return: List containing results
|
||||||
|
"""
|
||||||
|
url = FA_BASE_URL + '/search/'
|
||||||
|
form_data = {
|
||||||
|
'page': 1,
|
||||||
|
'q': search,
|
||||||
|
'do_search': 'Search',
|
||||||
|
'order-by': opts.order_by,
|
||||||
|
'order-direction': opts.order_direction,
|
||||||
|
'range': opts.range,
|
||||||
|
'mode': opts.match_mode
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set optional keys since they're judged on presence, not value
|
||||||
|
if opts.rating_general: form_data['rating-general'] = 'on'
|
||||||
|
if opts.rating_mature: form_data['rating-mature'] = 'on'
|
||||||
|
if opts.rating_adult: form_data['rating-adult'] = 'on'
|
||||||
|
if opts.type_art: form_data['type-art'] = 'on'
|
||||||
|
if opts.type_music: form_data['type-music'] = 'on'
|
||||||
|
if opts.type_flash: form_data['type-flash'] = 'on'
|
||||||
|
if opts.type_story: form_data['type-story'] = 'on'
|
||||||
|
if opts.type_photo: form_data['type-photo'] = 'on'
|
||||||
|
if opts.type_poetry: form_data['type-poetry'] = 'on'
|
||||||
|
|
||||||
|
submissions = []
|
||||||
|
while len(submissions) < results:
|
||||||
|
# Get our search results
|
||||||
|
res = self._req.post(url, data=form_data)
|
||||||
|
if res.status_code >= 400:
|
||||||
|
raise Exception('Got status code {} when trying to get URL {}'.format(res.status_code, url))
|
||||||
|
soup = BeautifulSoup(res.content, 'lxml')
|
||||||
|
|
||||||
|
# Get all the submissions on this page
|
||||||
|
items = soup.select('.gallery figure')
|
||||||
|
for item in items:
|
||||||
|
sub = self.get_submission(int(item.get('id')[4:])) # IDs look like 'sid-35908275', so we just skip the 'sid-'
|
||||||
|
|
||||||
|
# Try cache the preview URL early
|
||||||
|
try:
|
||||||
|
sub._preview_url = 'https:' + item.select('img')[0].get('src')
|
||||||
|
sub._has_preview = True
|
||||||
|
except IndexError:
|
||||||
|
sub._has_preview = False
|
||||||
|
|
||||||
|
submissions.append(sub)
|
||||||
|
|
||||||
|
if not soup.select('input[name=next_page]:not(.hidden)'):
|
||||||
|
# No further pages, stop searching
|
||||||
|
break
|
||||||
|
|
||||||
|
# Increment our page number
|
||||||
|
form_data['page'] = form_data['page'] + 1
|
||||||
|
|
||||||
|
return submissions[:results]
|
||||||
|
|
||||||
def __getstate__(self):
|
def __getstate__(self):
|
||||||
# Get a copy of the object state without the requests session
|
# Get a copy of the object state without the requests session
|
||||||
state = self.__dict__.copy()
|
state = self.__dict__.copy()
|
||||||
|
|||||||
2
setup.py
2
setup.py
@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='FAAPI',
|
name='FAAPI',
|
||||||
version='0.5.4',
|
version='0.6.0',
|
||||||
packages=find_packages(),
|
packages=find_packages(),
|
||||||
url='https://tem.party/gitea/tom/FAAPI',
|
url='https://tem.party/gitea/tom/FAAPI',
|
||||||
license='WTFPL',
|
license='WTFPL',
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user