44
55import requests
66from django .apps import apps
7+ from django .conf import settings
78from django .utils import timezone
89
910import app
1011from app .models import MediaTypes , Sources , Status
12+ from app .providers import services
1113from integrations .imports import helpers
1214from integrations .imports .helpers import MediaImportError , MediaImportUnexpectedError
1315
1416logger = logging .getLogger (__name__ )
1517
1618
17- def importer (username , user , mode ):
19+ def get_token (request ):
20+ """View for getting the AniList OAuth2 token."""
21+ domain = request .get_host ()
22+ scheme = request .scheme
23+ code = request .GET ["code" ]
24+
25+ url = "https://anilist.co/api/v2/oauth/token"
26+
27+ params = {
28+ "client_id" : settings .ANILIST_ID ,
29+ "client_secret" : settings .ANILIST_SECRET ,
30+ "code" : code ,
31+ "grant_type" : "authorization_code" ,
32+ "redirect_uri" : f"{ scheme } ://{ domain } /import/anilist/private" ,
33+ }
34+
35+ try :
36+ token_response = app .providers .services .api_request (
37+ "ANILIST" ,
38+ "POST" ,
39+ url ,
40+ params = params ,
41+ )
42+ except services .ProviderAPIError as error :
43+ if error .status_code == requests .codes .unauthorized :
44+ msg = "Invalid Anilist secret key."
45+ raise MediaImportError (msg ) from error
46+ raise
47+
48+ return {
49+ "access_token" : token_response ["access_token" ],
50+ "username" : get_username_from_oauth (token_response ["access_token" ]),
51+ }
52+
53+
54+ def get_username_from_oauth (access_token ):
55+ """Get AniList username from access token."""
56+ query = """
57+ query {
58+ Viewer {
59+ name
60+ }
61+ }
62+ """
63+
64+ headers = {
65+ "Authorization" : f"Bearer { access_token } " ,
66+ "Content-Type" : "application/json" ,
67+ }
68+
69+ try :
70+ response = app .providers .services .api_request (
71+ "ANILIST" ,
72+ "POST" ,
73+ "https://graphql.anilist.co" ,
74+ headers = headers ,
75+ params = {"query" : query },
76+ )
77+ except services .ProviderAPIError as error :
78+ if error .status_code == requests .codes .unauthorized :
79+ msg = "Invalid AniList access token."
80+ raise MediaImportError (msg ) from error
81+ raise
82+
83+ return response ["data" ]["Viewer" ]["name" ]
84+
85+
86+ def importer (token , user , mode , username ):
1887 """Import anime and manga ratings from Anilist."""
19- anilist_importer = AniListImporter (username , user , mode )
88+ anilist_importer = AniListImporter (token , user , mode , username )
2089 return anilist_importer .import_data ()
2190
2291
2392class AniListImporter :
2493 """Class to handle importing user data from AniList."""
2594
26- def __init__ (self , username , user , mode ):
95+ def __init__ (self , token , user , mode , username ):
2796 """Initialize the importer with username, user, and mode.
2897
2998 Args:
3099 username (str): AniList username to import from
100+ token (str): Encrypted access token for private imports (optional)
31101 user: Django user object to import data for
32102 mode (str): Import mode ("new" or "overwrite")
33103 """
34104 self .username = username
105+ self .token = token
35106 self .user = user
36107 self .mode = mode
37108 self .warnings = []
38109
110+ if self .token is not None :
111+ self .token = helpers .decrypt (self .token )
112+
39113 # Track existing media for "new" mode
40114 self .existing_media = helpers .get_existing_media (user )
41115
@@ -126,6 +200,14 @@ def import_data(self):
126200 variables = {"userName" : self .username }
127201 url = "https://graphql.anilist.co"
128202
203+ headers = {
204+ "Content-Type" : "application/json" ,
205+ "Accept" : "application/json" ,
206+ }
207+
208+ if self .token :
209+ headers ["Authorization" ] = f"Bearer { self .token } "
210+
129211 logger .info ("Fetching anime and manga from AniList account" )
130212
131213 try :
@@ -134,6 +216,7 @@ def import_data(self):
134216 "POST" ,
135217 url ,
136218 params = {"query" : query , "variables" : variables },
219+ headers = headers ,
137220 )
138221 except requests .exceptions .HTTPError as error :
139222 error_message = error .response .json ()["errors" ][0 ].get ("message" )
0 commit comments