Jelajahi Sumber

Now harmonizing description between services

Lucas Villeneuve 5 tahun lalu
induk
melakukan
eefa2a1e14
6 mengubah file dengan 208 tambahan dan 61 penghapusan
  1. 6 1
      myanimebot.py
  2. 32 32
      myanimebot/anilist.py
  3. 3 2
      myanimebot/discord.py
  4. 49 11
      myanimebot/myanimelist.py
  5. 61 14
      myanimebot/utils.py
  6. 57 1
      tests/test_myanimelist.py

+ 6 - 1
myanimebot.py

@@ -90,7 +90,12 @@ async def background_check_feed(asyncioloop):
 						pubDateRaw = datetime.strptime(feed_data.published, '%a, %d %b %Y %H:%M:%S %z').astimezone(globals.timezone)
 						DateTimezone = pubDateRaw.strftime("%z")[:3] + ':' + pubDateRaw.strftime("%z")[3:]
 						pubDate = pubDateRaw.strftime("%Y-%m-%d %H:%M:%S")
-						feed = myanimelist.build_feed_from_data(feed_data, user, None, pubDateRaw.timestamp(), feed_type)
+						if feed_type == 1:
+							media_type = utils.MediaType.MANGA
+						else:
+							media_type = utils.MediaType.ANIME
+
+						feed = myanimelist.build_feed_from_data(feed_data, user, None, pubDateRaw.timestamp(), media_type)
 						
 						cursor = globals.conn.cursor(buffered=True)
 						cursor.execute("SELECT published, title, url FROM t_feeds WHERE published=%s AND title=%s AND user=%s", [pubDate, feed.media.name, user.name])

+ 32 - 32
myanimebot/anilist.py

@@ -3,6 +3,7 @@ import datetime
 import time
 from enum import Enum
 from typing import Dict, List
+from discord import activity
 
 import requests
 
@@ -32,54 +33,50 @@ def get_media_name(activity):
     return ''
 
 
-def get_progress(activity):
+def get_progress(feed : utils.Feed, activity : dict):
+    ''' Tries to get progress from activity '''
+
     progress = activity["progress"]
     if progress is None:
-        return '?'
+        if feed.status == utils.MediaStatus.COMPLETED:
+            return feed.media.episodes
+        elif feed.status == utils.MediaStatus.PLANNING:
+            return '0'
+        else:
+            return '?'
     return progress
 
-
-def build_description_string(activity):
-    status_str = activity["status"].capitalize()
-    status = utils.MediaStatus.from_str(status_str)
-    progress = get_progress(activity)
-    episodes = ''
-    media_label = ''
-    media_type = utils.MediaType.from_str(activity["type"])
-
-    # TODO Manage Completed/Dropped/Planned episodes/chapters count
-    if status == utils.MediaStatus.CURRENT \
-       or status == utils.MediaStatus.REPEATING:
-        if media_type == utils.MediaType.ANIME:
-            episodes = activity["media"]["episodes"]
-            if episodes is None:
-                episodes = '?'
-            media_label = 'episodes'
-        elif media_type == utils.MediaType.MANGA:
-            episodes = activity["media"]["chapters"]
-            if episodes is None:
-                episodes = '?'
-            media_label = 'chapters'
-        return '{} | {} of {} {}'.format(status_str, progress, episodes, media_label)
-
+def get_number_episodes(activity, media_type : utils.MediaType):
+    episodes = '?'
+    if media_type == utils.MediaType.ANIME:
+        episodes = activity["media"]["episodes"]
+    elif media_type == utils.MediaType.MANGA:
+        episodes = activity["media"]["chapters"]
     else:
-        return '{}'.format(status_str)
+        raise NotImplementedError('Error: Unknown media type "{}"'.format(media_type))
+    if episodes is None:
+        episodes = '?'
+    return episodes
 
 
 def build_feed_from_activity(activity, user : utils.User) -> utils.Feed:
     if activity is None: return None
 
+    media_type = utils.MediaType.from_str(activity["type"])
+
     media = utils.Media(name=get_media_name(activity),
                         url=activity["media"]["siteUrl"],
-                        episodes=utils.Media.get_number_episodes(activity),
+                        episodes=get_number_episodes(activity, media_type),
                         image=activity["media"]["coverImage"]["large"],
-                        type=utils.MediaType.from_str(activity["media"]["type"]))
+                        type=media_type)
     feed = utils.Feed(service=utils.Service.ANILIST,
                         date_publication=datetime.datetime.fromtimestamp(activity["createdAt"], globals.timezone),
                         user=user,
                         status=utils.MediaStatus.from_str(activity["status"]),
-                        description=build_description_string(activity),
-                        media=media)
+                        description=None,
+                        media=media,
+                        progress=None)
+    feed.progress = get_progress(feed, activity)
     return feed
  
 
@@ -179,7 +176,7 @@ def get_latest_users_activities(users : List[utils.User], page: int, perPage = 5
         globals.logging.error('HTPP Error while getting the latest users\' AniList activities for {} on page {} with {} items per page. Error: {}'.format(users, page, perPage, e))
     except Exception as e:
         globals.logging.error('Unknown Error while getting the latest users\' AniList activities for {} on page {} with {} items per page. Error: {}'.format(users, page, perPage, e))
-    return []
+    return None
 
 
 def check_username_validity(username) -> bool:
@@ -312,6 +309,9 @@ async def process_new_activities(last_activity_date, users : List[utils.User]):
         # Get activities
         activities = get_latest_users_activities(users, page_number)
 
+        if activities == None: # An error occured, break the loop
+            return
+
         # Processing them
         for activity in activities:
             # Get time difference between now and activity creation date

+ 3 - 2
myanimebot/discord.py

@@ -19,11 +19,12 @@ def build_embed(feed : utils.Feed):
 		icon_url = globals.ANILIST_ICON_URL
 	else:
 		raise NotImplementedError('Unknown service {}'.format(feed.service))
-	description = "[{}]({})\n```{}```".format(utils.filter_name(feed.media.name), feed.media.url, feed.description)
+	description = utils.build_description_string(feed)
+	content = "[{}]({})\n```{}```".format(utils.filter_name(feed.media.name), feed.media.url, description)
 	profile_url_label = "{}'s {}".format(feed.user.name, service_name)
 
 	try:	
-		embed = discord.Embed(colour=0xEED000, url=feed.media.url, description=description, timestamp=feed.date_publication.astimezone(pytz.timezone("utc")))
+		embed = discord.Embed(colour=0xEED000, url=feed.media.url, description=content, timestamp=feed.date_publication.astimezone(pytz.timezone("utc")))
 		embed.set_thumbnail(url=feed.media.image)
 		embed.set_author(name=profile_url_label, url=profile_url, icon_url=icon_url)
 		embed.set_footer(text="MyAnimeBot", icon_url=globals.iconBot)

+ 49 - 11
myanimebot/myanimelist.py

@@ -8,7 +8,7 @@ import myanimebot.utils as utils
 import myanimebot.globals as globals
 
 def get_thumbnail(urlParam):
-    ''' Returns the MAL media thumnail from a link '''
+    ''' Returns the MAL media thumbnail from a link '''
 
     url = "/".join((urlParam).split("/")[:5])
 	
@@ -23,22 +23,60 @@ def get_thumbnail(urlParam):
 def build_feed_from_data(data, user : utils.User, image, pubDateRaw, type : utils.MediaType) -> utils.Feed:
     if data is None: return None
 
-    media = utils.Media(name=data.title,
-                        url=data.link,
-                        episodes=None,
-                        image=image,
-                        type=type)
-
     if data.description.startswith('-') :
-        if type == 1:
+        if type == utils.MediaType.MANGA:
             data.description = "Rereading " + data.description
         else:
             data.description = "Rewatching " + data.description								
 
+    status, progress, episodes = break_rss_description_string(data.description)
+
+    media = utils.Media(name=data.title,
+                        url=data.link,
+                        episodes=episodes,
+                        image=image,
+                        type=type)
+
     feed = utils.Feed(service=utils.Service.MAL,
                         date_publication=datetime.datetime.fromtimestamp(pubDateRaw, globals.timezone),
                         user=user,
-                        status=utils.MediaStatus.from_str(data.description),
-                        description=data.description,
-                        media=media)
+                        status=status,
+                        description=data.description, # TODO To remove, useless now
+                        media=media,
+                        progress=progress)
     return feed
+
+
+def break_rss_description_string(description : str):
+    ''' Break a MyAnimeList RSS description from a feed into a Status, the progress and the number of episodes '''
+
+    # Description example: "Completed - 12 of 12 episodes"
+
+    # Split the description starting from the dash
+    split_desc = description.rsplit('-', 1)
+    if (len(split_desc) != 2):
+        globals.logger.error("Error while trying to break MAL RSS description. No '-' found in '{}'.".format(description))
+        return None, None, None
+
+    status_str = split_desc[0]
+    episodes_progress_and_count = split_desc[1]
+    status = utils.MediaStatus.from_str(status_str)
+
+    # Split the second part of the string (E.g. "12 of 12 episodes") to get the progress
+    episodes_progress_and_count_split = episodes_progress_and_count.split('of', 1)
+    if (len(episodes_progress_and_count_split) != 2):
+        globals.logger.error("Error while trying to break MAL RSS description. No 'of' found between the progress and the episode count in '{}'.".format(description))
+        return None, None, None
+
+    progress = episodes_progress_and_count_split[0].strip()
+    episodes_count_str = episodes_progress_and_count_split[1].strip()
+
+    # Remove the episodes label from our string
+    episode_count_split = episodes_count_str.split(' ', 1)
+    if (len(episode_count_split) != 2):
+        globals.logger.error("Error while trying to break MAL RSS description. No space found between the episode count and the label episodes in '{}'.".format(description))
+        return None, None, None
+
+    episodes_count = episode_count_split[0]
+
+    return status, progress, episodes_count

+ 61 - 14
myanimebot/utils.py

@@ -28,6 +28,7 @@ class MediaType(Enum):
     ANIME="ANIME"
     MANGA="MANGA"
 
+
     @staticmethod
     def from_str(label: str):
         if label is None: raise TypeError
@@ -40,6 +41,15 @@ class MediaType(Enum):
             raise NotImplementedError('Error: Cannot convert "{}" to a MediaType'.format(label))
 
 
+    def get_media_count_type(self):
+        if self == MediaType.ANIME:
+            return 'episodes'
+        elif self == MediaType.MANGA:
+            return 'chapters'
+        else:
+            raise NotImplementedError('Unknown MediaType "{}"'.format(self))
+
+
 class MediaStatus(Enum):
     CURRENT=0
     PLANNING=1
@@ -96,20 +106,6 @@ class Media():
         self.image = image
         self.type = type
 
-    @staticmethod
-    def get_number_episodes(activity): # TODO Dont work for MAL
-        media_type = MediaType.from_str(activity["type"])
-        episodes = '?'
-        if media_type == MediaType.ANIME:
-            episodes = activity["media"]["episodes"]
-        elif media_type == MediaType.MANGA:
-            episodes = activity["media"]["chapters"]
-        else:
-            raise NotImplementedError('Error: Unknown media type "{}"'.format(media_type))
-        if episodes is None:
-            episodes = '?'
-        return episodes
-
 
 class Feed():
     def __init__(self,
@@ -118,6 +114,7 @@ class Feed():
                  user 			: User,
                  status			: MediaStatus,
                  description	: str, # TODO Need to change
+                 progress       : str,
                  media 			: Media
                  ):
         self.service = service
@@ -126,6 +123,7 @@ class Feed():
         self.status = status
         self.media = media
         self.description = description
+        self.progress = progress
 
 
 def replace_all(text : str, replace_dic : dict) -> str:
@@ -185,6 +183,55 @@ def truncate_end_show(media_name : str):
     return media_name
 
 
+def build_description_string(feed : Feed):
+    ''' Build and returns a string describing the feed '''
+
+    media_type_count = feed.media.type.get_media_count_type()
+
+    # if feed.service == Service.ANILIST:
+    if feed.status == MediaStatus.CURRENT \
+    or feed.status == MediaStatus.REPEATING:
+
+        if feed.media.type == MediaType.ANIME:
+            status_str = 'Watching'
+        elif feed.media.type == MediaType.MANGA:
+            status_str = 'Reading'
+        else:
+            raise NotImplementedError('Unknown MediaType: {}'.format(feed.media.type))
+
+        # Get feed status as a string
+        if feed.status == MediaStatus.REPEATING:
+            status_str = 'Re-{}'.format(status_str)
+
+    elif feed.status == MediaStatus.COMPLETED:
+        status_str = 'Completed'
+    elif feed.status == MediaStatus.PAUSED:
+        status_str = 'Paused'
+    elif feed.status == MediaStatus.DROPPED:
+        status_str = 'Dropped'
+    elif feed.status == MediaStatus.PLANNING:
+
+        if feed.media.type == MediaType.ANIME:
+            media_type_label = 'watch'
+        elif feed.media.type == MediaType.MANGA:
+            media_type_label = 'read'
+        else:
+            raise NotImplementedError('Unknown MediaType: {}'.format(feed.media.type))
+
+        status_str = 'Plans to {}'.format(media_type_label)
+    else:
+        raise NotImplementedError('Unknown MediaStatus: {}'.format(feed.status))
+
+    # Build the string
+    return '{} | {} of {} {}'.format(status_str, feed.progress, feed.media.episodes, media_type_count)
+
+    # elif feed.service == Service.MAL:
+    #     return feed.description
+    
+    # else:
+    #     raise NotImplementedError('Unknown Service: {}'.format(feed.service))
+
+
 def get_channels(server_id: int) -> dict:
     ''' Returns the registered channels for a server '''
 

+ 57 - 1
tests/test_myanimelist.py

@@ -1,6 +1,7 @@
 import pytest
 
-from myanimebot.myanimelist import get_thumbnail
+from myanimebot.myanimelist import break_rss_description_string, get_thumbnail
+from myanimebot.utils import MediaStatus
 
 def test_get_thumbnail():
     # Test manga
@@ -37,3 +38,58 @@ def test_get_thumbnail():
     with pytest.raises(Exception):
         get_thumbnail('https://anilist.co/anime/110277/Attack-on-Titan-Final-Season/')
 
+
+def test_break_rss_description_string():
+
+    status, progress, episodes = break_rss_description_string('Completed - 12 of 12 episodes')
+    assert status == MediaStatus.COMPLETED
+    assert progress == '12'
+    assert episodes == '12'
+
+    status, progress, episodes = break_rss_description_string('Completed - 192 of 192 chapters')
+    assert status == MediaStatus.COMPLETED
+    assert progress == '192'
+    assert episodes == '192'
+
+    status, progress, episodes = break_rss_description_string('Paused - 24 of 192 chapters')
+    assert status == MediaStatus.PAUSED
+    assert progress == '24'
+    assert episodes == '192'
+
+    status, progress, episodes = break_rss_description_string('On-hold - 23 of 27 episodes')
+    assert status == MediaStatus.PAUSED
+    assert progress == '23'
+    assert episodes == '27'
+
+    status, progress, episodes = break_rss_description_string('Dropped - 17 of 11 episodes')
+    assert status == MediaStatus.DROPPED
+    assert progress == '17'
+    assert episodes == '11'
+
+    status, progress, episodes = break_rss_description_string('Watching - 1 of 2 episodes')
+    assert status == MediaStatus.CURRENT
+    assert progress == '1'
+    assert episodes == '2'
+
+    status, progress, episodes = break_rss_description_string('Reading - 192 of ? chapters')
+    assert status == MediaStatus.CURRENT
+    assert progress == '192'
+    assert episodes == '?'
+
+    status, progress, episodes = break_rss_description_string('Rewatching - 0 of 1 episodes')
+    assert status == MediaStatus.REPEATING
+    assert progress == '0'
+    assert episodes == '1'
+
+    # Incorrect cases
+    status, progress, episodes = break_rss_description_string('Toto')
+    assert status == None and progress == None and episodes == None
+
+    status, progress, episodes = break_rss_description_string('Completed - blabla')
+    assert status == None and progress == None and episodes == None
+
+    status, progress, episodes = break_rss_description_string('Completed - 24 of 32')
+    assert status == None and progress == None and episodes == None
+
+    with pytest.raises(NotImplementedError):
+        break_rss_description_string('Toto - 24 of 32 episodes')