mirror of
https://github.com/Benexl/FastAnime.git
synced 2026-01-03 08:17:36 -08:00
feat(anilist): add account intergration
This commit is contained in:
@@ -8,7 +8,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class UserData:
|
||||
user_data = {"watch_history": {}, "animelist": []}
|
||||
user_data = {"watch_history": {}, "animelist": [], "user": {}}
|
||||
|
||||
def __init__(self):
|
||||
try:
|
||||
@@ -23,6 +23,10 @@ class UserData:
|
||||
self.user_data["watch_history"] = watch_history
|
||||
self._update_user_data()
|
||||
|
||||
def update_user_info(self, user: dict):
|
||||
self.user_data["user"] = user
|
||||
self._update_user_data()
|
||||
|
||||
def update_animelist(self, anime_list: list):
|
||||
self.user_data["animelist"] = list(set(anime_list))
|
||||
self._update_user_data()
|
||||
|
||||
@@ -3,6 +3,7 @@ import click
|
||||
from ...interfaces.anilist_interfaces import anilist as anilist_interface
|
||||
from ...utils.tools import QueryDict
|
||||
from .favourites import favourites
|
||||
from .login import loggin
|
||||
from .popular import popular
|
||||
from .random_anime import random_anime
|
||||
from .recent import recent
|
||||
@@ -20,6 +21,7 @@ commands = {
|
||||
"popular": popular,
|
||||
"favourites": favourites,
|
||||
"random": random_anime,
|
||||
"loggin": loggin,
|
||||
}
|
||||
|
||||
|
||||
|
||||
35
fastanime/cli/commands/anilist/login.py
Normal file
35
fastanime/cli/commands/anilist/login.py
Normal file
@@ -0,0 +1,35 @@
|
||||
import webbrowser
|
||||
|
||||
import click
|
||||
from rich import print
|
||||
from rich.prompt import Prompt
|
||||
|
||||
from ....anilist import AniList
|
||||
from ...config import Config
|
||||
from ...utils.tools import exit_app
|
||||
|
||||
|
||||
@click.command(help="Login to your anilist account")
|
||||
@click.option("--status", "-s", help="Whether you are logged in or not", is_flag=True)
|
||||
@click.pass_obj
|
||||
def loggin(config: Config, status):
|
||||
if status:
|
||||
is_logged_in = True if config.user else False
|
||||
message = (
|
||||
"You are logged in :happy:" if is_logged_in else "You arent logged in :sad:"
|
||||
)
|
||||
print(message)
|
||||
exit_app()
|
||||
if config.user:
|
||||
print("Already logged in :confused:")
|
||||
exit_app()
|
||||
# ---- new loggin -----
|
||||
print("A browser session will be opened")
|
||||
webbrowser.open(config.fastanime_anilist_app_login_url)
|
||||
print("Please paste the token provided here")
|
||||
token = Prompt.ask("Enter token")
|
||||
user = AniList.login_user(token)
|
||||
config.update_user(user)
|
||||
print("Successfully saved credentials")
|
||||
print(user)
|
||||
exit_app()
|
||||
@@ -11,6 +11,9 @@ from ..Utility.user_data_helper import user_data_helper
|
||||
class Config(object):
|
||||
anime_list: list
|
||||
watch_history: dict
|
||||
fastanime_anilist_app_login_url = (
|
||||
"https://anilist.co/api/v2/oauth/authorize?client_id=20148&response_type=token"
|
||||
)
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.load_config()
|
||||
@@ -59,9 +62,14 @@ class Config(object):
|
||||
# ---- setup user data ------
|
||||
self.watch_history: dict = user_data_helper.user_data.get("watch_history", {})
|
||||
self.anime_list: list = user_data_helper.user_data.get("animelist", [])
|
||||
self.user: dict = user_data_helper.user_data.get("user", {})
|
||||
|
||||
self.anime_provider = AnimeProvider(self.provider)
|
||||
|
||||
def update_user(self, user):
|
||||
self.user = user
|
||||
user_data_helper.update_user_info(user)
|
||||
|
||||
def update_watch_history(self, anime_id: int, episode: str | None):
|
||||
self.watch_history.update({str(anime_id): episode})
|
||||
user_data_helper.update_watch_history(self.watch_history)
|
||||
|
||||
@@ -17,6 +17,13 @@ class AnilistImage(TypedDict):
|
||||
large: str
|
||||
|
||||
|
||||
class AnilistUser(TypedDict):
|
||||
id: int
|
||||
name: str
|
||||
bannerImage: str | None
|
||||
avatar: AnilistImage
|
||||
|
||||
|
||||
class AnilistMediaTrailer(TypedDict):
|
||||
id: str
|
||||
site: str
|
||||
@@ -49,11 +56,6 @@ class AnilistMediaNextAiringEpisode(TypedDict):
|
||||
episode: int
|
||||
|
||||
|
||||
class AnilistUser(TypedDict):
|
||||
name: str
|
||||
avatar: AnilistImage
|
||||
|
||||
|
||||
class AnilistReview(TypedDict):
|
||||
summary: str
|
||||
user: AnilistUser
|
||||
|
||||
@@ -2,14 +2,18 @@
|
||||
This is the core module availing all the abstractions of the anilist api
|
||||
"""
|
||||
|
||||
from typing import Literal
|
||||
|
||||
import requests
|
||||
|
||||
from .anilist_data_schema import AnilistDataSchema
|
||||
from .anilist_data_schema import AnilistDataSchema, AnilistUser
|
||||
from .queries_graphql import (
|
||||
airing_schedule_query,
|
||||
anime_characters_query,
|
||||
anime_query,
|
||||
anime_relations_query,
|
||||
get_logged_in_user_query,
|
||||
media_list_query,
|
||||
most_favourite_query,
|
||||
most_popular_query,
|
||||
most_recently_updated_query,
|
||||
@@ -21,6 +25,7 @@ from .queries_graphql import (
|
||||
)
|
||||
|
||||
# from kivy.network.urlrequest import UrlRequestRequests
|
||||
ANILIST_ENDPOINT = "https://graphql.anilist.co"
|
||||
|
||||
|
||||
class AniListApi:
|
||||
@@ -28,6 +33,76 @@ class AniListApi:
|
||||
This class provides an abstraction for the anilist api
|
||||
"""
|
||||
|
||||
def login_user(self, token: str):
|
||||
self.token = token
|
||||
self.headers = {"Authorization": f"Bearer {self.token}"}
|
||||
user = self.get_logged_in_user()
|
||||
if not user:
|
||||
return
|
||||
if not user[0]:
|
||||
return
|
||||
user_info: AnilistUser = user[1]["data"]["Viewer"] # pyright:ignore
|
||||
self.user_id = user_info["id"] # pyright:ignore
|
||||
return user_info
|
||||
|
||||
def update_login_info(self, user: AnilistUser, token: str):
|
||||
self.token = token
|
||||
self.headers = {"Authorization": f"Bearer {self.token}"}
|
||||
self.user_id = user["id"]
|
||||
|
||||
def get_logged_in_user(self):
|
||||
if not self.headers:
|
||||
return
|
||||
return self._make_authenticated_request(get_logged_in_user_query)
|
||||
|
||||
def get_anime_list(
|
||||
self,
|
||||
status: Literal[
|
||||
"CURRENT", "PLANNING", "COMPLETED", "DROPPED", "PAUSED", "REPEATING"
|
||||
],
|
||||
):
|
||||
variables = {"status": status, "id": self.user_id}
|
||||
return self._make_authenticated_request(media_list_query, variables)
|
||||
|
||||
def _make_authenticated_request(self, query: str, variables: dict = {}):
|
||||
"""
|
||||
The core abstraction for getting authenticated data from the anilist api
|
||||
|
||||
Parameters:
|
||||
----------
|
||||
query:str
|
||||
a valid anilist graphql query
|
||||
variables:dict
|
||||
variables to pass to the anilist api
|
||||
"""
|
||||
# req=UrlRequestRequests(url, self.got_data,)
|
||||
try:
|
||||
# TODO: check if data is as expected
|
||||
response = requests.post(
|
||||
ANILIST_ENDPOINT,
|
||||
json={"query": query, "variables": variables},
|
||||
timeout=10,
|
||||
headers=self.headers,
|
||||
)
|
||||
anilist_data = response.json()
|
||||
return (True, anilist_data)
|
||||
except requests.exceptions.Timeout:
|
||||
return (
|
||||
False,
|
||||
{
|
||||
"Error": "Timeout Exceeded for connection there might be a problem with your internet or anilist is down."
|
||||
},
|
||||
) # type: ignore
|
||||
except requests.exceptions.ConnectionError:
|
||||
return (
|
||||
False,
|
||||
{
|
||||
"Error": "There might be a problem with your internet or anilist is down."
|
||||
},
|
||||
) # type: ignore
|
||||
except Exception as e:
|
||||
return (False, {"Error": f"{e}"}) # type: ignore
|
||||
|
||||
def get_data(
|
||||
self, query: str, variables: dict = {}
|
||||
) -> tuple[bool, AnilistDataSchema]:
|
||||
@@ -41,12 +116,13 @@ class AniListApi:
|
||||
variables:dict
|
||||
variables to pass to the anilist api
|
||||
"""
|
||||
url = "https://graphql.anilist.co"
|
||||
# req=UrlRequestRequests(url, self.got_data,)
|
||||
try:
|
||||
# TODO: check if data is as expected
|
||||
response = requests.post(
|
||||
url, json={"query": query, "variables": variables}, timeout=10
|
||||
ANILIST_ENDPOINT,
|
||||
json={"query": query, "variables": variables},
|
||||
timeout=10,
|
||||
)
|
||||
anilist_data: AnilistDataSchema = response.json()
|
||||
return (True, anilist_data)
|
||||
|
||||
@@ -3,6 +3,124 @@ This module contains all the preset queries for the sake of neatness and convini
|
||||
Mostly for internal usage
|
||||
"""
|
||||
|
||||
get_logged_in_user_query = """
|
||||
query{
|
||||
Viewer{
|
||||
id
|
||||
name
|
||||
bannerImage
|
||||
avatar {
|
||||
large
|
||||
medium
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
media_list_mutation = """
|
||||
mutation($mediaId:Int,$id:Int,$scoreRaw:Int,$repeat:Int,$progress:Int){
|
||||
SaveMediaListEntry(mediaId:$mediaId,id:$id,scoreRaw:$scoreRaw,progress:$progress,repeat:$repeat){
|
||||
id
|
||||
status
|
||||
mediaId
|
||||
score
|
||||
progress
|
||||
repeat
|
||||
startedAt {
|
||||
year
|
||||
month
|
||||
day
|
||||
}
|
||||
completedAt {
|
||||
year
|
||||
month
|
||||
day
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
media_list_query = """
|
||||
query ($userId: Int, $status: MediaListStatus) {
|
||||
Page {
|
||||
pageInfo {
|
||||
currentPage
|
||||
total
|
||||
}
|
||||
mediaList(userId: $userId, status: $status) {
|
||||
mediaId
|
||||
|
||||
media {
|
||||
id
|
||||
title {
|
||||
romaji
|
||||
english
|
||||
}
|
||||
coverImage {
|
||||
medium
|
||||
large
|
||||
}
|
||||
trailer {
|
||||
site
|
||||
id
|
||||
}
|
||||
popularity
|
||||
favourites
|
||||
averageScore
|
||||
episodes
|
||||
genres
|
||||
studios {
|
||||
nodes {
|
||||
name
|
||||
isAnimationStudio
|
||||
}
|
||||
}
|
||||
tags {
|
||||
name
|
||||
}
|
||||
startDate {
|
||||
year
|
||||
month
|
||||
day
|
||||
}
|
||||
endDate {
|
||||
year
|
||||
month
|
||||
day
|
||||
}
|
||||
status
|
||||
description
|
||||
nextAiringEpisode {
|
||||
timeUntilAiring
|
||||
airingAt
|
||||
episode
|
||||
}
|
||||
}
|
||||
status
|
||||
progress
|
||||
score
|
||||
repeat
|
||||
notes
|
||||
startedAt {
|
||||
year
|
||||
month
|
||||
day
|
||||
}
|
||||
completedAt {
|
||||
year
|
||||
month
|
||||
day
|
||||
}
|
||||
createdAt
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
optional_variables = "\
|
||||
$page:Int,\
|
||||
$sort:[MediaSort],\
|
||||
|
||||
Reference in New Issue
Block a user