diff --git a/catalog/book/models.py b/catalog/book/models.py index fecfd9a1..8cff2878 100644 --- a/catalog/book/models.py +++ b/catalog/book/models.py @@ -51,7 +51,7 @@ class Edition(Item): 'pages', 'contents', 'series', - 'producer', + 'imprint', ] subtitle = jsondata.CharField(null=True, blank=True, default=None) orig_title = jsondata.CharField(null=True, blank=True, default=None) @@ -66,7 +66,7 @@ class Edition(Item): series = jsondata.CharField(null=True, blank=True, default=None) contents = jsondata.CharField(null=True, blank=True, default=None) price = jsondata.FloatField(_("发表月份"), null=True, blank=True) - producer = jsondata.FloatField(_("发表月份"), null=True, blank=True) + imprint = jsondata.FloatField(_("发表月份"), null=True, blank=True) @property def isbn10(self): diff --git a/catalog/common/jsondata.py b/catalog/common/jsondata.py index ade72570..2cd1d398 100644 --- a/catalog/common/jsondata.py +++ b/catalog/common/jsondata.py @@ -129,7 +129,8 @@ class CharField(JSONFieldMixin, fields.CharField): class DateField(JSONFieldMixin, fields.DateField): def to_json(self, value): if value: - assert isinstance(value, (datetime, date)) + if not isinstance(value, (datetime, date)): + value = dateparse.parse_date(value) return value.strftime('%Y-%m-%d') def from_json(self, value): @@ -140,6 +141,8 @@ class DateField(JSONFieldMixin, fields.DateField): class DateTimeField(JSONFieldMixin, fields.DateTimeField): def to_json(self, value): if value: + if not isinstance(value, (datetime, date)): + value = dateparse.parse_date(value) if not timezone.is_aware(value): value = timezone.make_aware(value) return value.isoformat() diff --git a/catalog/common/models.py b/catalog/common/models.py index fb803469..5c572fb7 100644 --- a/catalog/common/models.py +++ b/catalog/common/models.py @@ -20,7 +20,7 @@ class SiteName(models.TextChoices): IMDB = 'imdb', _('IMDB') TMDB = 'tmdb', _('The Movie Database') Bandcamp = 'bandcamp', _('Bandcamp') - Spotify_Album = 'spotify', _('Spotify') + Spotify = 'spotify', _('Spotify') IGDB = 'igdb', _('IGDB') Steam = 'steam', _('Steam') Bangumi = 'bangumi', _('Bangumi') @@ -231,7 +231,7 @@ class Item(SoftDeleteMixin, PolymorphicModel): @property def url(self): - return f'/{self.url_path}/{self.url_id}' + return f'/{self.url_path}/{self.url_id}/' @property def class_name(self): @@ -239,7 +239,7 @@ class Item(SoftDeleteMixin, PolymorphicModel): @classmethod def get_by_url(cls, url_or_b62): - b62 = url_or_b62.split('/')[-1] + b62 = url_or_b62.strip().split('/')[-2] return cls.objects.get(uid=uuid.UUID(int=base62.decode(b62))) # def get_lookup_id(self, id_type: str) -> str: diff --git a/catalog/game/models.py b/catalog/game/models.py index 024ec85f..bd4281e0 100644 --- a/catalog/game/models.py +++ b/catalog/game/models.py @@ -1,10 +1,65 @@ from catalog.common import * +from django.utils.translation import gettext_lazy as _ +from django.db import models class Game(Item): category = ItemCategory.Game url_path = 'game' + demonstrative = _('这个游戏') igdb = PrimaryLookupIdDescriptor(IdType.IGDB) steam = PrimaryLookupIdDescriptor(IdType.Steam) douban_game = PrimaryLookupIdDescriptor(IdType.DoubanGame) - platforms = jsondata.ArrayField(default=list) + + METADATA_COPY_LIST = [ + 'title', + 'other_title', + 'developer', + 'publisher', + 'release_date', + 'genre', + 'platform', + 'brief', + ] + + other_title = jsondata.ArrayField( + models.CharField(blank=True, default='', max_length=500), + null=True, + blank=True, + default=list, + ) + + developer = jsondata.ArrayField( + models.CharField(blank=True, default='', max_length=500), + null=True, + blank=True, + default=list, + ) + + publisher = jsondata.ArrayField( + models.CharField(blank=True, default='', max_length=500), + null=True, + blank=True, + default=list, + ) + + release_date = jsondata.DateField( + auto_now=False, + auto_now_add=False, + null=True, + blank=True + ) + + genre = jsondata.ArrayField( + models.CharField(blank=True, default='', max_length=200), + null=True, + blank=True, + default=list, + ) + + platform = jsondata.ArrayField( + models.CharField(blank=True, default='', max_length=200), + null=True, + blank=True, + default=list, + ) diff --git a/catalog/models.py b/catalog/models.py index 11e75f88..75a92b05 100644 --- a/catalog/models.py +++ b/catalog/models.py @@ -1,3 +1,4 @@ +from .common.models import Item from .book.models import Edition, Work, Series from .movie.models import Movie from .tv.models import TVShow, TVSeason, TVEpisode diff --git a/catalog/movie/models.py b/catalog/movie/models.py index af180e24..052a34b6 100644 --- a/catalog/movie/models.py +++ b/catalog/movie/models.py @@ -10,12 +10,12 @@ class Movie(Item): tmdb_movie = PrimaryLookupIdDescriptor(IdType.TMDB_Movie) douban_movie = PrimaryLookupIdDescriptor(IdType.DoubanMovie) duration = jsondata.IntegerField(blank=True, default=None) + demonstrative = _('这部电影') METADATA_COPY_LIST = [ 'title', 'orig_title', 'other_title', - 'imdb_code', 'director', 'playwright', 'actor', @@ -33,7 +33,6 @@ class Movie(Item): ] orig_title = jsondata.CharField(_("original title"), blank=True, default='', max_length=500) other_title = jsondata.ArrayField(models.CharField(_("other title"), blank=True, default='', max_length=500), null=True, blank=True, default=list, ) - imdb_code = jsondata.CharField(blank=True, max_length=10, null=False, db_index=True, default='') director = jsondata.ArrayField(models.CharField(_("director"), blank=True, default='', max_length=200), null=True, blank=True, default=list, ) playwright = jsondata.ArrayField(models.CharField(_("playwright"), blank=True, default='', max_length=200), null=True, blank=True, default=list, ) actor = jsondata.ArrayField(models.CharField(_("actor"), blank=True, default='', max_length=200), null=True, blank=True, default=list, ) diff --git a/catalog/music/models.py b/catalog/music/models.py index c7f1e811..924e033b 100644 --- a/catalog/music/models.py +++ b/catalog/music/models.py @@ -1,12 +1,36 @@ from catalog.common import * +from django.utils.translation import gettext_lazy as _ +from django.db import models class Album(Item): url_path = 'album' category = ItemCategory.Music + demonstrative = _('这张专辑') barcode = PrimaryLookupIdDescriptor(IdType.GTIN) douban_music = PrimaryLookupIdDescriptor(IdType.DoubanMusic) spotify_album = PrimaryLookupIdDescriptor(IdType.Spotify_Album) - - class Meta: - proxy = True + METADATA_COPY_LIST = [ + 'title', + 'other_title', + 'album_type', + 'media', + 'disc_count', + 'artist', + 'genre', + 'release_date', + 'duration', + 'company', + 'track_list', + 'brief', + ] + release_date = jsondata.DateField(_('发行日期'), auto_now=False, auto_now_add=False, null=True, blank=True) + duration = jsondata.IntegerField(_("时长"), null=True, blank=True) + artist = jsondata.ArrayField(models.CharField(_("artist"), blank=True, default='', max_length=200), null=True, blank=True, default=list) + genre = jsondata.CharField(_("流派"), blank=True, default='', max_length=100) + company = jsondata.ArrayField(models.CharField(blank=True, default='', max_length=500), null=True, blank=True, default=list) + track_list = jsondata.TextField(_("曲目"), blank=True, default="") + other_title = jsondata.CharField(blank=True, default='', max_length=500) + album_type = jsondata.CharField(blank=True, default='', max_length=500) + media = jsondata.CharField(blank=True, default='', max_length=500) + disc_count = jsondata.IntegerField(blank=True, default='', max_length=500) diff --git a/catalog/sites/apple_podcast.py b/catalog/sites/apple_podcast.py index 17a3917a..2fd78bd8 100644 --- a/catalog/sites/apple_podcast.py +++ b/catalog/sites/apple_podcast.py @@ -8,6 +8,7 @@ _logger = logging.getLogger(__name__) @SiteManager.register class ApplePodcast(AbstractSite): + SITE_NAME = SiteName.ApplePodcast ID_TYPE = IdType.ApplePodcast URL_PATTERNS = [r"https://[^.]+.apple.com/\w+/podcast/*[^/?]*/id(\d+)"] WIKI_PROPERTY_ID = 'P5842' diff --git a/catalog/sites/bangumi.py b/catalog/sites/bangumi.py index 607c6398..6be9bd6b 100644 --- a/catalog/sites/bangumi.py +++ b/catalog/sites/bangumi.py @@ -8,6 +8,7 @@ _logger = logging.getLogger(__name__) @SiteManager.register class Bangumi(AbstractSite): + SITE_NAME = SiteName.Bangumi ID_TYPE = IdType.Bangumi URL_PATTERNS = [ r"https://bgm\.tv/subject/(\d+)", diff --git a/catalog/sites/douban_book.py b/catalog/sites/douban_book.py index 9313130c..8872d1f3 100644 --- a/catalog/sites/douban_book.py +++ b/catalog/sites/douban_book.py @@ -140,7 +140,7 @@ class DoubanBook(AbstractSite): imprint_elem = content.xpath( "//div[@id='info']//span[text()='出品方:']/following-sibling::a[1]/text()") - producer = imprint_elem[0].strip() if imprint_elem else None + imprint = imprint_elem[0].strip() if imprint_elem else None data = { 'title': title, @@ -160,7 +160,7 @@ class DoubanBook(AbstractSite): 'brief': brief, 'contents': contents, 'series': series, - 'producer': producer, + 'imprint': imprint, 'cover_image_url': img_url, } diff --git a/catalog/sites/douban_drama.py b/catalog/sites/douban_drama.py index 812c8c7f..8740a61c 100644 --- a/catalog/sites/douban_drama.py +++ b/catalog/sites/douban_drama.py @@ -9,6 +9,7 @@ _logger = logging.getLogger(__name__) @SiteManager.register class DoubanDrama(AbstractSite): + SITE_NAME = SiteName.Douban ID_TYPE = IdType.DoubanDrama URL_PATTERNS = [r"\w+://www.douban.com/location/drama/(\d+)/"] WIKI_PROPERTY_ID = 'P6443' diff --git a/catalog/sites/douban_game.py b/catalog/sites/douban_game.py index 0b8d247e..210002f2 100644 --- a/catalog/sites/douban_game.py +++ b/catalog/sites/douban_game.py @@ -10,6 +10,7 @@ _logger = logging.getLogger(__name__) @SiteManager.register class DoubanGame(AbstractSite): + SITE_NAME = SiteName.Douban ID_TYPE = IdType.DoubanGame URL_PATTERNS = [r"\w+://www\.douban\.com/game/(\d+)/{0,1}", r"\w+://m.douban.com/game/subject/(\d+)/{0,1}"] WIKI_PROPERTY_ID = '' diff --git a/catalog/sites/douban_movie.py b/catalog/sites/douban_movie.py index eb2a1878..d00ddc16 100644 --- a/catalog/sites/douban_movie.py +++ b/catalog/sites/douban_movie.py @@ -5,57 +5,15 @@ from catalog.tv.models import * import logging from django.db import models from django.utils.translation import gettext_lazy as _ -from .tmdb import TMDB_TV, search_tmdb_by_imdb_id +from .tmdb import TMDB_TV, search_tmdb_by_imdb_id, query_tmdb_tv_episode _logger = logging.getLogger(__name__) -class MovieGenreEnum(models.TextChoices): - DRAMA = 'Drama', _('剧情') - KIDS = 'Kids', _('儿童') - COMEDY = 'Comedy', _('喜剧') - BIOGRAPHY = 'Biography', _('传记') - ACTION = 'Action', _('动作') - HISTORY = 'History', _('历史') - ROMANCE = 'Romance', _('爱情') - WAR = 'War', _('战争') - SCI_FI = 'Sci-Fi', _('科幻') - CRIME = 'Crime', _('犯罪') - ANIMATION = 'Animation', _('动画') - WESTERN = 'Western', _('西部') - MYSTERY = 'Mystery', _('悬疑') - FANTASY = 'Fantasy', _('奇幻') - THRILLER = 'Thriller', _('惊悚') - ADVENTURE = 'Adventure', _('冒险') - HORROR = 'Horror', _('恐怖') - DISASTER = 'Disaster', _('灾难') - DOCUMENTARY = 'Documentary', _('纪录片') - MARTIAL_ARTS = 'Martial-Arts', _('武侠') - SHORT = 'Short', _('短片') - ANCIENT_COSTUM = 'Ancient-Costum', _('古装') - EROTICA = 'Erotica', _('情色') - SPORT = 'Sport', _('运动') - GAY_LESBIAN = 'Gay/Lesbian', _('同性') - OPERA = 'Opera', _('戏曲') - MUSIC = 'Music', _('音乐') - FILM_NOIR = 'Film-Noir', _('黑色电影') - MUSICAL = 'Musical', _('歌舞') - REALITY_TV = 'Reality-TV', _('真人秀') - FAMILY = 'Family', _('家庭') - TALK_SHOW = 'Talk-Show', _('脱口秀') - NEWS = 'News', _('新闻') - SOAP = 'Soap', _('肥皂剧') - TV_MOVIE = 'TV Movie', _('电视电影') - THEATRE = 'Theatre', _('舞台艺术') - OTHER = 'Other', _('其他') - - -# MovieGenreTranslator = ChoicesDictGenerator(MovieGenreEnum) - - @SiteManager.register class DoubanMovie(AbstractSite): + SITE_NAME = SiteName.Douban ID_TYPE = IdType.DoubanMovie URL_PATTERNS = [r"\w+://movie\.douban\.com/subject/(\d+)/{0,1}", r"\w+://m.douban.com/movie/subject/(\d+)/{0,1}"] WIKI_PROPERTY_ID = '?' @@ -109,30 +67,16 @@ class DoubanMovie(AbstractSite): "//div[@id='info']//span[text()='主演']/following-sibling::span[1]/a/text()") actor = list(map(lambda a: a[:200], actor_elem)) if actor_elem else None - # construct genre translator - genre_translator = {} - attrs = [attr for attr in dir(MovieGenreEnum) if '__' not in attr] - for attr in attrs: - genre_translator[getattr(MovieGenreEnum, attr).label] = getattr( - MovieGenreEnum, attr).value - genre_elem = content.xpath("//span[@property='v:genre']/text()") + genre = [] if genre_elem: - genre = [] for g in genre_elem: g = g.split(' ')[0] if g == '紀錄片': # likely some original data on douban was corrupted g = '纪录片' elif g == '鬼怪': g = '惊悚' - if g in genre_translator: - genre.append(genre_translator[g]) - elif g in genre_translator.values(): - genre.append(g) - else: - _logger.error(f'unable to map genre {g}') - else: - genre = None + genre.append(g) showtime_elem = content.xpath( "//span[@property='v:initialReleaseDate']/text()") @@ -230,7 +174,7 @@ class DoubanMovie(AbstractSite): 'year': year, 'duration': duration, 'season_number': season, - 'episodes': episodes, + 'episode_count': episodes, 'single_episode_length': single_episode_length, 'brief': brief, 'is_series': is_series, @@ -252,8 +196,11 @@ class DoubanMovie(AbstractSite): pd.metadata['preferred_model'] = 'TVSeason' tmdb_show_id = res_data['tv_episode_results'][0]['show_id'] if res_data['tv_episode_results'][0]['episode_number'] != 1: - _logger.error(f'Douban Movie {self.url} mapping to unexpected imdb episode {imdb_code}') - # TODO correct the IMDB id + _logger.warning(f'Douban Movie {self.url} mapping to unexpected imdb episode {imdb_code}') + resp = query_tmdb_tv_episode(tmdb_show_id, res_data['tv_episode_results'][0]['season_number'], 1) + imdb_code = resp['external_ids']['imdb_id'] + _logger.warning(f'Douban Movie {self.url} re-mapped to imdb episode {imdb_code}') + pd.lookup_ids[IdType.IMDB] = imdb_code if tmdb_show_id: pd.metadata['required_resources'] = [{ diff --git a/catalog/sites/douban_music.py b/catalog/sites/douban_music.py index 3ae28b48..1d8038f0 100644 --- a/catalog/sites/douban_music.py +++ b/catalog/sites/douban_music.py @@ -10,6 +10,7 @@ _logger = logging.getLogger(__name__) @SiteManager.register class DoubanMusic(AbstractSite): + SITE_NAME = SiteName.Douban ID_TYPE = IdType.DoubanMusic URL_PATTERNS = [r"\w+://music\.douban\.com/subject/(\d+)/{0,1}", r"\w+://m.douban.com/music/subject/(\d+)/{0,1}"] WIKI_PROPERTY_ID = '' @@ -56,51 +57,48 @@ class DoubanMusic(AbstractSite): brief = '\n'.join([e.strip() for e in brief_elem[0].xpath( './text()')]) if brief_elem else None - gtin = None - isrc = None - other_info = {} - other_elem = content.xpath( - "//div[@id='info']//span[text()='又名:']/following-sibling::text()[1]") - if other_elem: - other_info['又名'] = other_elem[0].strip() - other_elem = content.xpath( - "//div[@id='info']//span[text()='专辑类型:']/following-sibling::text()[1]") - if other_elem: - other_info['专辑类型'] = other_elem[0].strip() - other_elem = content.xpath( - "//div[@id='info']//span[text()='介质:']/following-sibling::text()[1]") - if other_elem: - other_info['介质'] = other_elem[0].strip() - other_elem = content.xpath( - "//div[@id='info']//span[text()='ISRC:']/following-sibling::text()[1]") - if other_elem: - other_info['ISRC'] = other_elem[0].strip() - isrc = other_elem[0].strip() - other_elem = content.xpath( - "//div[@id='info']//span[text()='条形码:']/following-sibling::text()[1]") - if other_elem: - other_info['条形码'] = other_elem[0].strip() - gtin = other_elem[0].strip() - other_elem = content.xpath( - "//div[@id='info']//span[text()='碟片数:']/following-sibling::text()[1]") - if other_elem: - other_info['碟片数'] = other_elem[0].strip() - img_url_elem = content.xpath("//div[@id='mainpic']//img/@src") img_url = img_url_elem[0].strip() if img_url_elem else None - pd = ResourceContent(metadata={ + data = { 'title': title, 'artist': artist, 'genre': genre, 'release_date': release_date, 'duration': None, - 'company': company, + 'company': [company], 'track_list': track_list, 'brief': brief, - 'other_info': other_info, 'cover_image_url': img_url - }) + } + gtin = None + isrc = None + other_elem = content.xpath( + "//div[@id='info']//span[text()='又名:']/following-sibling::text()[1]") + if other_elem: + data['other_title'] = other_elem[0].strip() + other_elem = content.xpath( + "//div[@id='info']//span[text()='专辑类型:']/following-sibling::text()[1]") + if other_elem: + data['album_type'] = other_elem[0].strip() + other_elem = content.xpath( + "//div[@id='info']//span[text()='介质:']/following-sibling::text()[1]") + if other_elem: + data['media'] = other_elem[0].strip() + other_elem = content.xpath( + "//div[@id='info']//span[text()='ISRC:']/following-sibling::text()[1]") + if other_elem: + isrc = other_elem[0].strip() + other_elem = content.xpath( + "//div[@id='info']//span[text()='条形码:']/following-sibling::text()[1]") + if other_elem: + gtin = other_elem[0].strip() + other_elem = content.xpath( + "//div[@id='info']//span[text()='碟片数:']/following-sibling::text()[1]") + if other_elem: + data['disc_count'] = other_elem[0].strip() + + pd = ResourceContent(metadata=data) if gtin: pd.lookup_ids[IdType.GTIN] = gtin if isrc: diff --git a/catalog/sites/igdb.py b/catalog/sites/igdb.py index bd90962d..973889fa 100644 --- a/catalog/sites/igdb.py +++ b/catalog/sites/igdb.py @@ -39,6 +39,7 @@ def search_igdb_by_3p_url(steam_url): @SiteManager.register class IGDB(AbstractSite): + SITE_NAME = SiteName.IGDB ID_TYPE = IdType.IGDB URL_PATTERNS = [r"\w+://www\.igdb\.com/games/([a-zA-Z0-9\-_]+)"] WIKI_PROPERTY_ID = '?' @@ -90,9 +91,9 @@ class IGDB(AbstractSite): steam_url = website['url'] pd = ResourceContent(metadata={ 'title': r['name'], - 'other_title': None, - 'developer': developer, - 'publisher': publisher, + 'other_title': [], + 'developer': [developer], + 'publisher': [publisher], 'release_date': release_date, 'genre': genre, 'platform': platform, diff --git a/catalog/sites/imdb.py b/catalog/sites/imdb.py index 42a20f75..900d4289 100644 --- a/catalog/sites/imdb.py +++ b/catalog/sites/imdb.py @@ -10,6 +10,7 @@ _logger = logging.getLogger(__name__) @SiteManager.register class IMDB(AbstractSite): + SITE_NAME = SiteName.IMDB ID_TYPE = IdType.IMDB URL_PATTERNS = [r'\w+://www.imdb.com/title/(tt\d+)'] WIKI_PROPERTY_ID = '?' diff --git a/catalog/sites/spotify.py b/catalog/sites/spotify.py index 0d38f08d..23f68120 100644 --- a/catalog/sites/spotify.py +++ b/catalog/sites/spotify.py @@ -21,6 +21,7 @@ spotify_token_expire_time = time.time() @SiteManager.register class Spotify(AbstractSite): + SITE_NAME = SiteName.Spotify ID_TYPE = IdType.Spotify_Album URL_PATTERNS = [r'\w+://open\.spotify\.com/album/([a-zA-Z0-9]+)'] WIKI_PROPERTY_ID = '?' @@ -106,7 +107,7 @@ def get_spotify_token(): invoke_spotify_token() return spotify_token - + def is_spotify_token_expired(): global spotify_token_expire_time return True if spotify_token_expire_time <= time.time() else False diff --git a/catalog/sites/steam.py b/catalog/sites/steam.py index b5c42227..029e885f 100644 --- a/catalog/sites/steam.py +++ b/catalog/sites/steam.py @@ -10,6 +10,7 @@ _logger = logging.getLogger(__name__) @SiteManager.register class Steam(AbstractSite): + SITE_NAME = SiteName.Steam ID_TYPE = IdType.Steam URL_PATTERNS = [r"\w+://store\.steampowered\.com/app/(\d+)"] WIKI_PROPERTY_ID = '?' diff --git a/catalog/sites/tmdb.py b/catalog/sites/tmdb.py index 3cfa87c0..c6bd9d3e 100644 --- a/catalog/sites/tmdb.py +++ b/catalog/sites/tmdb.py @@ -20,6 +20,12 @@ def search_tmdb_by_imdb_id(imdb_id): return res_data +def query_tmdb_tv_episode(tv, season, episode): + tmdb_api_url = f"https://api.themoviedb.org/3/tv/{tv}/season/{season}/episode/{episode}?api_key={settings.TMDB_API3_KEY}&language=zh-CN&append_to_response=external_ids" + res_data = BasicDownloader(tmdb_api_url).download().json() + return res_data + + def _copy_dict(s, key_map): d = {} for src, dst in key_map.items(): @@ -27,39 +33,9 @@ def _copy_dict(s, key_map): return d -genre_map = { - 'Sci-Fi & Fantasy': 'Sci-Fi', - 'War & Politics': 'War', - '儿童': 'Kids', - '冒险': 'Adventure', - '剧情': 'Drama', - '动作': 'Action', - '动作冒险': 'Action', - '动画': 'Animation', - '历史': 'History', - '喜剧': 'Comedy', - '奇幻': 'Fantasy', - '家庭': 'Family', - '恐怖': 'Horror', - '悬疑': 'Mystery', - '惊悚': 'Thriller', - '战争': 'War', - '新闻': 'News', - '爱情': 'Romance', - '犯罪': 'Crime', - '电视电影': 'TV Movie', - '真人秀': 'Reality-TV', - '科幻': 'Sci-Fi', - '纪录': 'Documentary', - '肥皂剧': 'Soap', - '脱口秀': 'Talk-Show', - '西部': 'Western', - '音乐': 'Music', -} - - @SiteManager.register class TMDB_Movie(AbstractSite): + SITE_NAME = SiteName.TMDB ID_TYPE = IdType.TMDB_Movie URL_PATTERNS = [r'\w+://www.themoviedb.org/movie/(\d+)'] WIKI_PROPERTY_ID = '?' @@ -98,8 +74,7 @@ class TMDB_Movie(AbstractSite): # in minutes duration = res_data['runtime'] if res_data['runtime'] else None - genre = list(map(lambda x: genre_map[x['name']] if x['name'] - in genre_map else 'Other', res_data['genres'])) + genre = [x['name'] for x in res_data['genres']] language = list(map(lambda x: x['name'], res_data['spoken_languages'])) brief = res_data['overview'] @@ -199,8 +174,8 @@ class TMDB_TV(AbstractSite): # in minutes duration = res_data['runtime'] if res_data['runtime'] else None - genre = list(map(lambda x: genre_map[x['name']] if x['name'] - in genre_map else 'Other', res_data['genres'])) + genre = [x['name'] for x in res_data['genres']] + language = list(map(lambda x: x['name'], res_data['spoken_languages'])) brief = res_data['overview'] @@ -248,6 +223,7 @@ class TMDB_TV(AbstractSite): 'language': language, 'year': year, 'duration': duration, + 'season_count': res_data['number_of_seasons'], 'season': None, 'episodes': None, 'single_episode_length': None, @@ -292,7 +268,7 @@ class TMDB_TVSeason(AbstractSite): d = BasicDownloader(api_url).download().json() if not d.get('id'): raise ParseError('id') - pd = ResourceContent(metadata=_copy_dict(d, {'name': 'title', 'overview': 'brief', 'air_date': 'air_date', 'season_number': 0, 'external_ids': 0})) + pd = ResourceContent(metadata=_copy_dict(d, {'name': 'title', 'overview': 'brief', 'air_date': 'air_date', 'season_number': 0, 'external_ids': []})) pd.metadata['required_resources'] = [{ 'model': 'TVShow', 'id_type': IdType.TMDB_TV, diff --git a/catalog/templates/album.html b/catalog/templates/album.html index 16df8f0e..ad764336 100644 --- a/catalog/templates/album.html +++ b/catalog/templates/album.html @@ -12,8 +12,106 @@ {% block details %} +
+
+ {% if item.rating and item.rating_number >= 5 %} + + {{ item.rating }} + ({{ item.rating_number }}人评分) + {% else %} + {% trans '评分:评分人数不足' %} + {% endif %} +
+
{% if item.artist %}{% trans '艺术家:' %} + {% for artist in item.artist %} + 5 %}style="display: none;" {% endif %}> + {{ artist }} + {% if not forloop.last %} / {% endif %} + + {% endfor %} + {% if item.artist|length > 5 %} + {% trans '更多' %} + + {% endif %} + {% endif %}
+
{% if item.company %}{% trans '发行方:' %} + {% for company in item.company %} + 5 %}style="display: none;" {% endif %}> + {{ company }} + {% if not forloop.last %} / {% endif %} + + {% endfor %} + {% if item.company|length > 5 %} + {% trans '更多' %} + + {% endif %} + {% endif %}
+
{% if item.release_date %} + {% trans '发行日期:' %}{{ item.release_date }} + {% endif %} +
+
{% if item.duration %} + {% trans '时长:' %}{{ item.get_duration_display }} + {% endif %} +
+
{% if item.genre %} + {% trans '流派:' %}{{ item.genre }} + {% endif %} +
+
{% if item.barcode %} + {% trans '条形码:' %}{{ item.barcode }} + {% endif %} +
+ +
+
+
{% if item.other_title %} + {% trans '又名:' %}{{ item.other_title }} + {% endif %} +
+
{% if item.album_type %} + {% trans '专辑类型:' %}{{ item.album_type }} + {% endif %} +
+
{% if item.media %} + {% trans '介质:' %}{{ item.media }} + {% endif %} +
+
{% if item.disc_count %} + {% trans '碟片数:' %}{{ item.disc_count }} + {% endif %} +
+ + {% if item.last_editor and item.last_editor.preference.show_last_edit or user.is_staff %} +
{% trans '最近编辑者:' %}{{ item.last_editor | default:"" }}
+ {% endif %} + +
+ {% trans '编辑这个作品' %} + {% if user.is_staff %} + / {% trans '删除' %} + {% endif %} +
+
{% endblock %} {% block sidebar %} +{% if item.get_embed_link %} + +{% endif %} {% endblock %} diff --git a/catalog/templates/edition.html b/catalog/templates/edition.html index 22ad05b5..548892c5 100644 --- a/catalog/templates/edition.html +++ b/catalog/templates/edition.html @@ -43,6 +43,7 @@
{% if item.binding %}{% trans '装帧:' %}{{ item.binding }}{% endif %}
{% if item.price %}{% trans '定价:' %}{{ item.price }}{% endif %}
{% if item.pages %}{% trans '页数:' %}{{ item.pages }}{% endif %}
+
{% if item.imprint %}{% trans '出品方:' %}{{ item.imprint }}{% endif %}
{% if item.other_info %} {% for k, v in item.other_info.items %}
diff --git a/catalog/templates/game.html b/catalog/templates/game.html index 16df8f0e..41690755 100644 --- a/catalog/templates/game.html +++ b/catalog/templates/game.html @@ -12,6 +12,100 @@ {% block details %} +
+
+ {% if item.rating and item.rating_number >= 5 %} + + {{ item.rating }} + ({{ item.rating_number }}人评分) + {% else %} + {% trans '评分:评分人数不足' %} + {% endif %} +
+ +
{% if item.other_title %}{% trans '别名:' %} + {% for other_title in item.other_title %} + 5 %}style="display: none;" {% endif %}> + {{ other_title }} + {% if not forloop.last %} / {% endif %} + + {% endfor %} + {% if item.other_title|length > 5 %} + {% trans '更多' %} + + {% endif %} + {% endif %} +
+ +
+ {% if item.genre %}{% trans '类型:' %} + {% for genre in item.genre %} + {{ genre }}{% if not forloop.last %} / {% endif %} + {% endfor %} + {% endif %} +
+ +
+ {% if item.developer %}{% trans '开发商:' %} + {% for developer in item.developer %} + {{ developer }}{% if not forloop.last %} / {% endif %} + {% endfor %} + {% endif %} +
+ +
+ {% if item.publisher %}{% trans '发行商:' %} + {% for publisher in item.publisher %} + {{ publisher }}{% if not forloop.last %} / {% endif %} + {% endfor %} + {% endif %} +
+ +
{% if item.release_date %} + {% trans '发行日期:' %}{{ item.release_date }} + {% endif %} +
+ + {% if item.other_info %} + {% for k, v in item.other_info.items %} +
+ {{ k }}:{{ v | urlize }} +
+ {% endfor %} + {% endif %} + +
+
+ +
+ {% if item.platform %}{% trans '平台:' %} + {% for platform in item.platform %} + {{ platform }}{% if not forloop.last %} / {% endif %} + {% endfor %} + {% endif %} +
+ + + + {% if item.last_editor and item.last_editor.preference.show_last_edit or user.is_staff %} +
{% trans '最近编辑者:' %}{{ item.last_editor | default:"" }}
+ {% endif %} + +
+ {% trans '编辑这个游戏' %} + + {% if user.is_staff %} + / {% trans '删除' %} + {% endif %} +
+
{% endblock %} diff --git a/catalog/templates/item_base.html b/catalog/templates/item_base.html index 1d45d172..89254e90 100644 --- a/catalog/templates/item_base.html +++ b/catalog/templates/item_base.html @@ -52,6 +52,7 @@
+ {% block title %}
{{ item.title }} @@ -61,6 +62,7 @@ {% endfor %}
+ {% endblock %} {% block details %}
@@ -112,6 +114,16 @@
{% endif %} + {% if item.track_list %} +
+
{% trans '曲目' %}
+

{{ item.track_list | linebreaksbr }}

+ +
+ {% endif %} +
{% trans '标记' %}
{% trans '全部标记' %} @@ -152,10 +164,10 @@
{% if mark.shelf_type %}
+ {% trans '我' %}{% trans mark.shelf_label %} {% if mark.rating %} {% endif %} - {% trans '我' %}{% trans mark.shelf_label %} {% if mark.visibility > 0 %} {% endif %} diff --git a/catalog/templates/movie.html b/catalog/templates/movie.html index 16df8f0e..d5cb0542 100644 --- a/catalog/templates/movie.html +++ b/catalog/templates/movie.html @@ -10,8 +10,162 @@ {% load strip_scheme %} {% load thumb %} +{% block title %} +
+ {% if item.season_number %} + {{ item.title }} {% trans '第' %}{{ item.season_number|apnumber }}{% trans '季' %} {{ item.orig_title }} Season {{ item.season_number }} + + {% if item.year %}({{ item.year }}){% endif %} + + {% else %} + {{ item.title }} {{ item.orig_title }} + + {% if item.year %}({{ item.year }}){% endif %} + + {% endif %} + + {% for res in item.external_resources.all %} + + {{ res.site_name.label }} + + {% endfor %} +
+{% endblock %} + {% block details %} +
+
+ {% if item.rating and item.rating_number >= 5 %} + + {{ item.rating }} + ({{ item.rating_number }}人评分) + {% else %} + {% trans '评分:评分人数不足' %} + {% endif %} +
+
{% if item.imdb %} + {% trans 'IMDb:' %}{{ item.imdb }} + {% endif %} +
+
{% if item.director %}{% trans '导演:' %} + {% for director in item.director %} + 5 %}style="display: none;" {% endif %}> + {{ director }} + {% if not forloop.last %} / {% endif %} + + {% endfor %} + {% if item.director|length > 5 %} + {% trans '更多' %} + + {% endif %} + {% endif %}
+
{% if item.playwright %}{% trans '编剧:' %} + {% for playwright in item.playwright %} + 5 %}style="display: none;" {% endif %}> + {{ playwright }} + {% if not forloop.last %} / {% endif %} + + {% endfor %} + {% if item.playwright|length > 5 %} + {% trans '更多' %} + + {% endif %} + {% endif %}
+
{% if item.actor %}{% trans '主演:' %} + {% for actor in item.actor %} + 5 %}style="display: none;"{% endif %}> + {{ actor }} + {% if not forloop.last %} / {% endif %} + + {% endfor %} + + {% if item.actor|length > 5 %} + {% trans '更多' %} + + {% endif %} + + {% endif %}
+
{% if item.genre %}{% trans '类型:' %} + {% for genre in item.genre %} + {{ genre }}{% if not forloop.last %} / {% endif %} + {% endfor %} + {% endif %}
+
{% if item.area %}{% trans '制片国家/地区:' %} + {% for area in item.area %} + {{ area }}{% if not forloop.last %} / {% endif %} + {% endfor %} + {% endif %}
+
{% if item.language %}{% trans '语言:' %} + {% for language in item.language %} + {{ language }}{% if not forloop.last %} / {% endif %} + {% endfor %} + {% endif %}
+ +
+
+
{% if item.duration %}{% trans '片长:' %}{{ item.duration }}{% endif %}
+
{% if item.season_count %}{% trans '季数:' %}{{ item.season_count }}{% endif %}
+
{% if item.episode_count %}{% trans '集数:' %}{{ item.episode_count }}{% endif %}
+
{% if item.single_episode_length %}{% trans '单集长度:' %}{{ item.single_episode_length }}{% endif %}
+ +
{% if item.showtime %}{% trans '上映时间:' %} + {% for showtime in item.showtime %} + {% for time, region in showtime.items %} + {{ time }}{% if region != '' %}({{ region }}){% endif %} + {% endfor %} + {% if not forloop.last %} / {% endif %} + {% endfor %} + {% endif %}
+
{% if item.other_title %}{% trans '又名:' %} + {% for other_title in item.other_title %} + {{ other_title }}{% if not forloop.last %} / {% endif %} + {% endfor %} + {% endif %}
+
{% if item.site %}{% trans '网站:' %} + {{ item.site|strip_scheme }} + {% endif %}
+ {% if item.other_info %} + {% for k, v in item.other_info.items %} +
+ {{ k }}:{{ v | urlize }} +
+ {% endfor %} + {% endif %} + + + {% if item.last_editor and item.last_editor.preference.show_last_edit or user.is_staff %} +
{% trans '最近编辑者:' %}{{ item.last_editor | default:"" }}
+ {% endif %} + +
+ {% trans '编辑这部电影' %} + {% if user.is_staff %} + / {% trans '删除' %} + {% endif %} +
+
{% endblock %} diff --git a/catalog/templates/tvseason.html b/catalog/templates/tvseason.html index 16df8f0e..d5cb0542 100644 --- a/catalog/templates/tvseason.html +++ b/catalog/templates/tvseason.html @@ -10,8 +10,162 @@ {% load strip_scheme %} {% load thumb %} +{% block title %} +
+ {% if item.season_number %} + {{ item.title }} {% trans '第' %}{{ item.season_number|apnumber }}{% trans '季' %} {{ item.orig_title }} Season {{ item.season_number }} + + {% if item.year %}({{ item.year }}){% endif %} + + {% else %} + {{ item.title }} {{ item.orig_title }} + + {% if item.year %}({{ item.year }}){% endif %} + + {% endif %} + + {% for res in item.external_resources.all %} + + {{ res.site_name.label }} + + {% endfor %} +
+{% endblock %} + {% block details %} +
+
+ {% if item.rating and item.rating_number >= 5 %} + + {{ item.rating }} + ({{ item.rating_number }}人评分) + {% else %} + {% trans '评分:评分人数不足' %} + {% endif %} +
+
{% if item.imdb %} + {% trans 'IMDb:' %}{{ item.imdb }} + {% endif %} +
+
{% if item.director %}{% trans '导演:' %} + {% for director in item.director %} + 5 %}style="display: none;" {% endif %}> + {{ director }} + {% if not forloop.last %} / {% endif %} + + {% endfor %} + {% if item.director|length > 5 %} + {% trans '更多' %} + + {% endif %} + {% endif %}
+
{% if item.playwright %}{% trans '编剧:' %} + {% for playwright in item.playwright %} + 5 %}style="display: none;" {% endif %}> + {{ playwright }} + {% if not forloop.last %} / {% endif %} + + {% endfor %} + {% if item.playwright|length > 5 %} + {% trans '更多' %} + + {% endif %} + {% endif %}
+
{% if item.actor %}{% trans '主演:' %} + {% for actor in item.actor %} + 5 %}style="display: none;"{% endif %}> + {{ actor }} + {% if not forloop.last %} / {% endif %} + + {% endfor %} + + {% if item.actor|length > 5 %} + {% trans '更多' %} + + {% endif %} + + {% endif %}
+
{% if item.genre %}{% trans '类型:' %} + {% for genre in item.genre %} + {{ genre }}{% if not forloop.last %} / {% endif %} + {% endfor %} + {% endif %}
+
{% if item.area %}{% trans '制片国家/地区:' %} + {% for area in item.area %} + {{ area }}{% if not forloop.last %} / {% endif %} + {% endfor %} + {% endif %}
+
{% if item.language %}{% trans '语言:' %} + {% for language in item.language %} + {{ language }}{% if not forloop.last %} / {% endif %} + {% endfor %} + {% endif %}
+ +
+
+
{% if item.duration %}{% trans '片长:' %}{{ item.duration }}{% endif %}
+
{% if item.season_count %}{% trans '季数:' %}{{ item.season_count }}{% endif %}
+
{% if item.episode_count %}{% trans '集数:' %}{{ item.episode_count }}{% endif %}
+
{% if item.single_episode_length %}{% trans '单集长度:' %}{{ item.single_episode_length }}{% endif %}
+ +
{% if item.showtime %}{% trans '上映时间:' %} + {% for showtime in item.showtime %} + {% for time, region in showtime.items %} + {{ time }}{% if region != '' %}({{ region }}){% endif %} + {% endfor %} + {% if not forloop.last %} / {% endif %} + {% endfor %} + {% endif %}
+
{% if item.other_title %}{% trans '又名:' %} + {% for other_title in item.other_title %} + {{ other_title }}{% if not forloop.last %} / {% endif %} + {% endfor %} + {% endif %}
+
{% if item.site %}{% trans '网站:' %} + {{ item.site|strip_scheme }} + {% endif %}
+ {% if item.other_info %} + {% for k, v in item.other_info.items %} +
+ {{ k }}:{{ v | urlize }} +
+ {% endfor %} + {% endif %} + + + {% if item.last_editor and item.last_editor.preference.show_last_edit or user.is_staff %} +
{% trans '最近编辑者:' %}{{ item.last_editor | default:"" }}
+ {% endif %} + +
+ {% trans '编辑这部电影' %} + {% if user.is_staff %} + / {% trans '删除' %} + {% endif %} +
+
{% endblock %} diff --git a/catalog/templates/tvshow.html b/catalog/templates/tvshow.html index 16df8f0e..d5cb0542 100644 --- a/catalog/templates/tvshow.html +++ b/catalog/templates/tvshow.html @@ -10,8 +10,162 @@ {% load strip_scheme %} {% load thumb %} +{% block title %} +
+ {% if item.season_number %} + {{ item.title }} {% trans '第' %}{{ item.season_number|apnumber }}{% trans '季' %} {{ item.orig_title }} Season {{ item.season_number }} + + {% if item.year %}({{ item.year }}){% endif %} + + {% else %} + {{ item.title }} {{ item.orig_title }} + + {% if item.year %}({{ item.year }}){% endif %} + + {% endif %} + + {% for res in item.external_resources.all %} + + {{ res.site_name.label }} + + {% endfor %} +
+{% endblock %} + {% block details %} +
+
+ {% if item.rating and item.rating_number >= 5 %} + + {{ item.rating }} + ({{ item.rating_number }}人评分) + {% else %} + {% trans '评分:评分人数不足' %} + {% endif %} +
+
{% if item.imdb %} + {% trans 'IMDb:' %}{{ item.imdb }} + {% endif %} +
+
{% if item.director %}{% trans '导演:' %} + {% for director in item.director %} + 5 %}style="display: none;" {% endif %}> + {{ director }} + {% if not forloop.last %} / {% endif %} + + {% endfor %} + {% if item.director|length > 5 %} + {% trans '更多' %} + + {% endif %} + {% endif %}
+
{% if item.playwright %}{% trans '编剧:' %} + {% for playwright in item.playwright %} + 5 %}style="display: none;" {% endif %}> + {{ playwright }} + {% if not forloop.last %} / {% endif %} + + {% endfor %} + {% if item.playwright|length > 5 %} + {% trans '更多' %} + + {% endif %} + {% endif %}
+
{% if item.actor %}{% trans '主演:' %} + {% for actor in item.actor %} + 5 %}style="display: none;"{% endif %}> + {{ actor }} + {% if not forloop.last %} / {% endif %} + + {% endfor %} + + {% if item.actor|length > 5 %} + {% trans '更多' %} + + {% endif %} + + {% endif %}
+
{% if item.genre %}{% trans '类型:' %} + {% for genre in item.genre %} + {{ genre }}{% if not forloop.last %} / {% endif %} + {% endfor %} + {% endif %}
+
{% if item.area %}{% trans '制片国家/地区:' %} + {% for area in item.area %} + {{ area }}{% if not forloop.last %} / {% endif %} + {% endfor %} + {% endif %}
+
{% if item.language %}{% trans '语言:' %} + {% for language in item.language %} + {{ language }}{% if not forloop.last %} / {% endif %} + {% endfor %} + {% endif %}
+ +
+
+
{% if item.duration %}{% trans '片长:' %}{{ item.duration }}{% endif %}
+
{% if item.season_count %}{% trans '季数:' %}{{ item.season_count }}{% endif %}
+
{% if item.episode_count %}{% trans '集数:' %}{{ item.episode_count }}{% endif %}
+
{% if item.single_episode_length %}{% trans '单集长度:' %}{{ item.single_episode_length }}{% endif %}
+ +
{% if item.showtime %}{% trans '上映时间:' %} + {% for showtime in item.showtime %} + {% for time, region in showtime.items %} + {{ time }}{% if region != '' %}({{ region }}){% endif %} + {% endfor %} + {% if not forloop.last %} / {% endif %} + {% endfor %} + {% endif %}
+
{% if item.other_title %}{% trans '又名:' %} + {% for other_title in item.other_title %} + {{ other_title }}{% if not forloop.last %} / {% endif %} + {% endfor %} + {% endif %}
+
{% if item.site %}{% trans '网站:' %} + {{ item.site|strip_scheme }} + {% endif %}
+ {% if item.other_info %} + {% for k, v in item.other_info.items %} +
+ {{ k }}:{{ v | urlize }} +
+ {% endfor %} + {% endif %} + + + {% if item.last_editor and item.last_editor.preference.show_last_edit or user.is_staff %} +
{% trans '最近编辑者:' %}{{ item.last_editor | default:"" }}
+ {% endif %} + +
+ {% trans '编辑这部电影' %} + {% if user.is_staff %} + / {% trans '删除' %} + {% endif %} +
+
{% endblock %} diff --git a/catalog/tv/models.py b/catalog/tv/models.py index a6fbc634..c759729c 100644 --- a/catalog/tv/models.py +++ b/catalog/tv/models.py @@ -26,27 +26,96 @@ For now, we follow Douban convention, but keep an eye on it in case it breaks it """ from catalog.common import * from django.db import models +from django.utils.translation import gettext_lazy as _ class TVShow(Item): category = ItemCategory.TV url_path = 'tv' + demonstrative = _('这部剧集') imdb = PrimaryLookupIdDescriptor(IdType.IMDB) tmdb_tv = PrimaryLookupIdDescriptor(IdType.TMDB_TV) imdb = PrimaryLookupIdDescriptor(IdType.IMDB) - season_count = jsondata.IntegerField(blank=True, default=None) + season_count = jsondata.IntegerField(null=True) + + METADATA_COPY_LIST = [ + 'title', + 'season_count', + 'orig_title', + 'other_title', + 'director', + 'playwright', + 'actor', + 'genre', + 'showtime', + 'site', + 'area', + 'language', + 'year', + 'duration', + 'season_number', + 'single_episode_length', + 'brief', + ] + orig_title = jsondata.CharField(_("original title"), blank=True, default='', max_length=500) + other_title = jsondata.ArrayField(models.CharField(_("other title"), blank=True, default='', max_length=500), null=True, blank=True, default=list, ) + director = jsondata.ArrayField(models.CharField(_("director"), blank=True, default='', max_length=200), null=True, blank=True, default=list, ) + playwright = jsondata.ArrayField(models.CharField(_("playwright"), blank=True, default='', max_length=200), null=True, blank=True, default=list, ) + actor = jsondata.ArrayField(models.CharField(_("actor"), blank=True, default='', max_length=200), null=True, blank=True, default=list, ) + genre = jsondata.ArrayField(models.CharField(_("genre"), blank=True, default='', max_length=50), null=True, blank=True, default=list, ) # , choices=MovieGenreEnum.choices + showtime = jsondata.ArrayField(null=True, blank=True, default=list, ) + site = jsondata.URLField(_('site url'), blank=True, default='', max_length=200) + area = jsondata.ArrayField(models.CharField(_("country or region"), blank=True, default='', max_length=100, ), null=True, blank=True, default=list, ) + language = jsondata.ArrayField(models.CharField(blank=True, default='', max_length=100, ), null=True, blank=True, default=list, ) + year = jsondata.IntegerField(null=True, blank=True) + season_number = jsondata.IntegerField(null=True, blank=True) + single_episode_length = jsondata.IntegerField(null=True, blank=True) + duration = jsondata.CharField(blank=True, default='', max_length=200) class TVSeason(Item): category = ItemCategory.TV url_path = 'tv/season' + demonstrative = _('这部剧集') douban_movie = PrimaryLookupIdDescriptor(IdType.DoubanMovie) imdb = PrimaryLookupIdDescriptor(IdType.IMDB) tmdb_tvseason = PrimaryLookupIdDescriptor(IdType.TMDB_TVSeason) show = models.ForeignKey(TVShow, null=True, on_delete=models.SET_NULL, related_name='seasons') - season_number = models.PositiveIntegerField() - episode_count = jsondata.IntegerField(blank=True, default=None) - METADATA_COPY_LIST = ['title', 'brief', 'season_number', 'episode_count'] + season_number = models.PositiveIntegerField(null=True) + episode_count = models.PositiveIntegerField(null=True) + + METADATA_COPY_LIST = [ + 'title', + 'orig_title', + 'other_title', + 'director', + 'playwright', + 'actor', + 'genre', + 'showtime', + 'site', + 'area', + 'language', + 'year', + 'duration', + 'season_number', + 'episode_count', + 'single_episode_length', + 'brief', + ] + orig_title = jsondata.CharField(_("original title"), blank=True, default='', max_length=500) + other_title = jsondata.ArrayField(models.CharField(_("other title"), blank=True, default='', max_length=500), null=True, blank=True, default=list, ) + director = jsondata.ArrayField(models.CharField(_("director"), blank=True, default='', max_length=200), null=True, blank=True, default=list, ) + playwright = jsondata.ArrayField(models.CharField(_("playwright"), blank=True, default='', max_length=200), null=True, blank=True, default=list, ) + actor = jsondata.ArrayField(models.CharField(_("actor"), blank=True, default='', max_length=200), null=True, blank=True, default=list, ) + genre = jsondata.ArrayField(models.CharField(_("genre"), blank=True, default='', max_length=50), null=True, blank=True, default=list, ) # , choices=MovieGenreEnum.choices + showtime = jsondata.ArrayField(null=True, blank=True, default=list, ) + site = jsondata.URLField(_('site url'), blank=True, default='', max_length=200) + area = jsondata.ArrayField(models.CharField(_("country or region"), blank=True, default='', max_length=100, ), null=True, blank=True, default=list, ) + language = jsondata.ArrayField(models.CharField(blank=True, default='', max_length=100, ), null=True, blank=True, default=list, ) + year = jsondata.IntegerField(null=True, blank=True) + single_episode_length = jsondata.IntegerField(null=True, blank=True) + duration = jsondata.CharField(blank=True, default='', max_length=200) def update_linked_items_from_external_resource(self, resource): """add Work from resource.metadata['work'] if not yet""" @@ -63,6 +132,6 @@ class TVEpisode(Item): url_path = 'tv/episode' show = models.ForeignKey(TVShow, null=True, on_delete=models.SET_NULL, related_name='episodes') season = models.ForeignKey(TVSeason, null=True, on_delete=models.SET_NULL, related_name='episodes') - episode_number = models.PositiveIntegerField() + episode_number = models.PositiveIntegerField(null=True) imdb = PrimaryLookupIdDescriptor(IdType.IMDB) METADATA_COPY_LIST = ['title', 'brief', 'episode_number'] diff --git a/catalog/tv/tests.py b/catalog/tv/tests.py index b43e0f98..ffc7ab05 100644 --- a/catalog/tv/tests.py +++ b/catalog/tv/tests.py @@ -79,6 +79,13 @@ class DoubanMovieTVTestCase(TestCase): p3 = SiteManager.get_site_by_url(url3).get_resource_ready() self.assertEqual(p3.item.__class__.__name__, 'TVShow') + @use_local_response + def test_scrape_fix_imdb(self): + url = 'https://movie.douban.com/subject/35597581/' + item = SiteManager.get_site_by_url(url).get_resource_ready().item + # this douban links to S6E3, we'll reset it to S6E1 to keep consistant + self.assertEqual(item.imdb, 'tt21599650') + class MultiTVSitesTestCase(TestCase): @use_local_response diff --git a/catalog/urls.py b/catalog/urls.py index 744074f1..9be0f947 100644 --- a/catalog/urls.py +++ b/catalog/urls.py @@ -1,9 +1,20 @@ from django.urls import path, re_path from .api import api from .views import * +from .models import * + + +def _get_all_url_paths(): + paths = ['item'] + for cls in Item.__subclasses__(): + p = getattr(cls, 'url_path', None) + if p: + paths.append(p) + res = "|".join(paths) + return res urlpatterns = [ - path("", api.urls), - re_path('book/(?P[A-Za-z0-9]{21,22})/', retrieve, name='retrieve'), + re_path(r'(?P' + _get_all_url_paths() + ')/(?P[A-Za-z0-9]{21,22})/', retrieve, name='retrieve'), + path("api/", api.urls), ] diff --git a/catalog/views.py b/catalog/views.py index 2a5df161..ed2fab53 100644 --- a/catalog/views.py +++ b/catalog/views.py @@ -27,9 +27,12 @@ from journal.models import Mark _logger = logging.getLogger(__name__) -def retrieve(request, uid): +def retrieve(request, item_path, item_uid): if request.method == 'GET': - item = get_object_or_404(Edition, uid=base62.decode(uid)) + item = get_object_or_404(Item, uid=base62.decode(item_uid)) + item_url = f'/{item_path}/{item_uid}/' + if item.url != item_url: + return redirect(item.url) mark = None review = None mark_list = None diff --git a/journal/models.py b/journal/models.py index 0ebdc6f5..04501d94 100644 --- a/journal/models.py +++ b/journal/models.py @@ -327,23 +327,33 @@ class ShelfManager: # metadata=None means no change if not item: raise ValueError('empty item') - lastqm = self._shelf_member_for_item(item) - lastqmm = lastqm.metadata if lastqm else None - lastq = lastqm._shelf if lastqm else None - lastqt = lastq.shelf_type if lastq else None + last_shelfmember = self._shelf_member_for_item(item) + last_shelf = last_shelfmember._shelf if last_shelfmember else None + last_metadata = last_shelfmember.metadata if last_shelfmember else None + last_visibility = last_shelfmember.visibility if last_shelfmember else None shelf = self.get_shelf(item.category, shelf_type) if shelf_type else None - if lastq != shelf: - if lastq: - lastq.remove_item(item) + changed = False + if last_shelf != shelf: # change shelf + changed = True + if last_shelf: + last_shelf.remove_item(item) if shelf: shelf.append_item(item, visibility=visibility, metadata=metadata or {}) - elif metadata is not None: - lastqm.metadata = metadata - lastqm.save() - elif lastqm: - metadata = lastqm.metadata - if lastqt != shelf_type or (lastqt and metadata != lastqmm): - ShelfLogEntry.objects.create(owner=self.owner, shelf=shelf, item=item, metadata=metadata or {}) + elif last_shelf is None: + raise ValueError('empty shelf') + else: + if metadata is not None and metadata != last_metadata: # change metadata + changed = True + last_shelfmember.metadata = metadata + last_shelfmember.visibility = visibility + last_shelfmember.save() + elif visibility != last_visibility: # change visibility + last_shelfmember.visibility = visibility + last_shelfmember.save() + if changed: + if metadata is None: + metadata = last_metadata or {} + ShelfLogEntry.objects.create(owner=self.owner, shelf=shelf, item=item, metadata=metadata) def get_log(self): return ShelfLogEntry.objects.filter(owner=self.owner).order_by('timestamp') @@ -488,6 +498,10 @@ class Mark: def shelf_label(self): return self.shelfmember._shelf.shelf_label if self.shelfmember else None + @property + def created_time(self): + return self.shelfmember.created_time if self.shelfmember else None + @property def visibility(self): return self.shelfmember.visibility if self.shelfmember else None diff --git a/journal/tests.py b/journal/tests.py index 08460112..45995f30 100644 --- a/journal/tests.py +++ b/journal/tests.py @@ -62,9 +62,13 @@ class ShelfTest(TestCase): log = shelf_manager.get_log_for_item(book1) self.assertEqual(log.count(), 4) self.assertEqual(log.last().metadata, {'progress': 10}) - shelf_manager.move_item(book1, ShelfType.STARTED, metadata={'progress': 100}) + shelf_manager.move_item(book1, ShelfType.STARTED, metadata={'progress': 90}) log = shelf_manager.get_log_for_item(book1) self.assertEqual(log.count(), 5) + self.assertEqual(Mark(user, book1).visibility, 0) + shelf_manager.move_item(book1, ShelfType.STARTED, metadata={'progress': 90}, visibility=1) + self.assertEqual(Mark(user, book1).visibility, 1) + self.assertEqual(shelf_manager.get_log_for_item(book1).count(), 5) class TagTest(TestCase): diff --git a/test_data/https___api_themoviedb_org_3_find_tt15389382_api_key_19890604_language_zh_CN_external_source_imdb_id b/test_data/https___api_themoviedb_org_3_find_tt15389382_api_key_19890604_language_zh_CN_external_source_imdb_id new file mode 100644 index 00000000..c1c9f5f4 --- /dev/null +++ b/test_data/https___api_themoviedb_org_3_find_tt15389382_api_key_19890604_language_zh_CN_external_source_imdb_id @@ -0,0 +1 @@ +{"movie_results":[],"person_results":[],"tv_results":[],"tv_episode_results":[{"id":3854678,"name":"双胞胎贝丝","overview":"夏茉和莫蒂沉浸在全新的超逼真游戏机。","media_type":"tv_episode","vote_average":8.8,"vote_count":8,"air_date":"2022-09-18","episode_number":3,"production_code":"","runtime":22,"season_number":6,"show_id":60625,"still_path":"/8ZCNAxkDo67GtcBJBDIERdywnar.jpg"}],"tv_season_results":[]} \ No newline at end of file diff --git a/test_data/https___api_themoviedb_org_3_tv_60625_api_key_19890604_language_zh_CN_append_to_response_external_ids_credits b/test_data/https___api_themoviedb_org_3_tv_60625_api_key_19890604_language_zh_CN_append_to_response_external_ids_credits new file mode 100644 index 00000000..c79afb8d --- /dev/null +++ b/test_data/https___api_themoviedb_org_3_tv_60625_api_key_19890604_language_zh_CN_append_to_response_external_ids_credits @@ -0,0 +1 @@ +{"adult":false,"backdrop_path":"/uGy4DCmM33I7l86W7iCskNkvmLD.jpg","created_by":[{"id":57194,"credit_id":"57bf826fc3a3684d25001b3c","name":"Dan Harmon","gender":2,"profile_path":"/gDwFosoyPTd0pmnKParzGj3kaMg.jpg"},{"id":1245733,"credit_id":"57bf827dc3a3684d3400183e","name":"Justin Roiland","gender":2,"profile_path":"/wYApP38aXe6ZcEtlBAfNRxJTQQi.jpg"}],"episode_run_time":[22],"first_air_date":"2013-12-02","genres":[{"id":16,"name":"动画"},{"id":35,"name":"喜剧"},{"id":10765,"name":"Sci-Fi & Fantasy"},{"id":10759,"name":"动作冒险"}],"homepage":"http://www.adultswim.com/videos/rick-and-morty","id":60625,"in_production":true,"languages":["en"],"last_air_date":"2022-12-11","last_episode_to_air":{"air_date":"2022-12-11","episode_number":10,"id":3891509,"name":"第 10 集","overview":"","production_code":"","runtime":22,"season_number":6,"show_id":60625,"still_path":"/i9DrzFapnE2LTBOlR0xD4pejHm3.jpg","vote_average":8.5,"vote_count":4},"name":"瑞克和莫蒂","next_episode_to_air":null,"networks":[{"id":80,"name":"Adult Swim","logo_path":"/tHZPHOLc6iF27G34cAZGPsMtMSy.png","origin_country":"US"}],"number_of_episodes":61,"number_of_seasons":6,"origin_country":["US"],"original_language":"en","original_name":"Rick and Morty","overview":"天才兼疯子科学家Rick在失踪多年后突然回到女儿Beth的身边,并且在她的车库里搞了一个科学实验室。Rick有一把“传送门枪”,可以穿越到宇宙的各个次元。孙子Morty莫名其妙就成了他的助手,经常被拖进他的自制太空船内,跟他一同开展各种疯狂刺激的宇宙冒险。女儿女婿对他的疯狂行为感到不满,却又对这个天才科学家无可奈何。","popularity":779.68,"poster_path":"/mPbGr0IjNxLsxXIJ7QeUtZK0RLH.jpg","production_companies":[{"id":6760,"logo_path":"/h3syDHowNmk61FzlW2GPY0vJCFh.png","name":"Williams Street","origin_country":"US"},{"id":8300,"logo_path":null,"name":"Harmonious Claptrap","origin_country":"US"},{"id":32542,"logo_path":null,"name":"Starburns Industries","origin_country":"US"},{"id":47394,"logo_path":null,"name":"Justin Roiland's Solo Vanity Card Productions","origin_country":"US"},{"id":131124,"logo_path":null,"name":"Green Portal Productions","origin_country":"US"}],"production_countries":[{"iso_3166_1":"US","name":"United States of America"}],"seasons":[{"air_date":"2016-10-25","episode_count":35,"id":106178,"name":"特别篇","overview":"","poster_path":"/3my0MrOKCSYMw8VfLiiM9k00bdF.jpg","season_number":0},{"air_date":"2013-12-02","episode_count":11,"id":60059,"name":"第 1 季","overview":"天才兼疯子科学家Rick在失踪多年后突然回到女儿Beth的身边,并且在她的车库里搞了一个科学实验室。Rick有一把“传送门枪”,可以穿越到宇宙的各个次元。外孙Morty莫名其妙就成了他的助手,经常被拖进他的自制太空船内,跟他一同开展各种疯狂刺激的宇宙冒险。女儿女婿对他的 疯狂行为感到不满,却又对这个天才科学家无可奈何。","poster_path":"/8BXUZ0nnR3DZsf30DYMFHfuTxxi.jpg","season_number":1},{"air_date":"2015-07-26","episode_count":10,"id":66738,"name":"第 2 季","overview":"瑞克和莫蒂决定解冻时间后,便得应付外星寄生虫、多个杰瑞的分身及摇摇欲坠且可能不复存在的次元。","poster_path":"/ezeKvdZOsnMomE5zFUO4yh79v4O.jpg","season_number":2},{"air_date":"2017-04-01","episode_count":10,"id":86926,"name":"第 3 季","overview":"在第3 季中,瑞克和莫蒂到亚特兰蒂斯旅行放松。瑞克还把自己变成腌菜,并且跟总统一较高下。","poster_path":"/rX55zDxWkNuG4wyQyfMBkSk75df.jpg","season_number":3},{"air_date":"2019-11-09","episode_count":10,"id":128112,"name":"第 4 季","overview":"Rick(瑞克)和Morty(莫蒂)讲述了地球C-137(S1-06之后转移到的时空N/A(可在S2-02Jerry寄托所中Rick填的表格中看到)之前的时空由于Rick的药水使除了Morty一家的人类都变成了柯南伯格式的怪物)的Rick和Morty的冒险故事,但有时也 有Rick和Summer,Morty和Jerry这样的冒险组合。","poster_path":"/2vuwC1ojHSBxtyGnARXTMemxlnK.jpg","season_number":4},{"air_date":"2021-06-20","episode_count":10,"id":188470,"name":"第 5 季","overview":"该剧主要围绕于愤世嫉俗的疯狂科学家瑞克·桑切斯和他容易受挫折的外孙莫蒂·史密斯在自己的生活和其他异空间穿梭的奇妙冒险。","poster_path":"/mOKY7hFafyIPwChKpHdoxre54eI.jpg","season_number":5},{"air_date":"2022-09-04","episode_count":10,"id":302503,"name":"第 6 季","overview":"现在是第六季,瑞克和莫蒂回归了!从我们丢下他们的地方开始,他们的运气越来越差。他们会重新开始更多的冒险吗?或者他们会被尿的海洋卷走!谁知道呢? !尿!家庭!阴谋!一群恐龙!更多的尿!又一个不能错过你最喜欢的节目的季节。","poster_path":"/cvhNj9eoRBe5SxjCbQTkh05UP5K.jpg","season_number":6}],"spoken_languages":[{"english_name":"English","iso_639_1":"en","name":"English"}],"status":"Returning Series","tagline":"科学讲得通 但家庭不行","type":"Scripted","vote_average":8.731,"vote_count":7531,"external_ids":{"imdb_id":"tt2861424","freebase_mid":"/m/0z6p24j","freebase_id":null,"tvdb_id":275274,"tvrage_id":33381,"wikidata_id":"Q15659308","facebook_id":"RickandMorty","instagram_id":"rickandmorty","twitter_id":"RickandMorty"},"credits":{"cast":[{"adult":false,"gender":2,"id":1245733,"known_for_department":"Acting","name":"Justin Roiland","original_name":"Justin Roiland","popularity":21.112,"profile_path":"/wYApP38aXe6ZcEtlBAfNRxJTQQi.jpg","character":"Rick Sanchez / Morty Smith (voice)","credit_id":"5347f65ac3a368670b0013f3","order":0},{"adult":false,"gender":2,"id":51382,"known_for_department":"Acting","name":"Chris Parnell","original_name":"Chris Parnell","popularity":11.809,"profile_path":"/aGzA5l9S89qLkhpSdjmkerFdZt0.jpg","character":"Jerry Smith (voice)","credit_id":"5347f684c3a368672d001411","order":1},{"adult":false,"gender":1,"id":176823,"known_for_department":"Acting","name":"Spencer Grammer","original_name":"Spencer Grammer","popularity":11.939,"profile_path":"/1L8Y45RJo2YxUXl6ldIowQay1V7.jpg","character":"Summer Smith (voice)","credit_id":"5347f72ec3a368670b00140b","order":2},{"adult":false,"gender":1,"id":49001,"known_for_department":"Acting","name":"Sarah Chalke","original_name":"Sarah Chalke","popularity":26.691,"profile_path":"/ycwiu89cpjqCtSNC5FjbJggjj5R.jpg","character":"Beth Smith (voice)","credit_id":"5347f693c3a3686723001484","order":3}],"crew":[{"adult":false,"gender":1,"id":1267190,"known_for_department":"Writing","name":"Heather Anne Campbell","original_name":"Heather Anne Campbell","popularity":1.435,"profile_path":"/t6kM3FUszf9hUJSNLn0DvHeXr71.jpg","credit_id":"63156ba702842000830da273","department":"Production","job":"Co-Executive Producer"},{"adult":false,"gender":0,"id":1588012,"known_for_department":"Production","name":"Steve Levy","original_name":"Steve Levy","popularity":0.6,"profile_path":null,"credit_id":"63156bd00cb335007d3cd035","department":"Production","job":"Co-Executive Producer"},{"adult":false,"gender":2,"id":2643127,"known_for_department":"Writing","name":"Albro Lundy IV","original_name":"Albro Lundy IV","popularity":1.176,"profile_path":null,"credit_id":"63156bde9408ec007bed41cc","department":"Production","job":"Co-Executive Producer"},{"adult":false,"gender":2,"id":1350906,"known_for_department":"Writing","name":"Alex Rubens","original_name":"Alex Rubens","popularity":2.148,"profile_path":"/xbLPUVKrStq5AZuGIqbrupTK3MC.jpg","credit_id":"63156bf29408ec007bed41db","department":"Production","job":"Co-Executive Producer"},{"adult":false,"gender":2,"id":1180701,"known_for_department":"Acting","name":"Nick Rutherford","original_name":"Nick Rutherford","popularity":2.2,"profile_path":"/zkZWfrqUSgmk7fNG38AK61s1azD.jpg","credit_id":"63156bfef1b571007dba6370","department":"Production","job":"Co-Executive Producer"},{"adult":false,"gender":0,"id":2484730,"known_for_department":"Writing","name":"James Siciliano","original_name":"James Siciliano","popularity":1.62,"profile_path":null,"credit_id":"63156c16b3e627007e3699bc","department":"Production","job":"Co-Executive Producer"},{"adult":false,"gender":0,"id":1853062,"known_for_department":"Production","name":"Monica Mitchell","original_name":"Monica Mitchell","popularity":0.694,"profile_path":null,"credit_id":"63156c44b3e627007e3699d1","department":"Production","job":"Supervising Producer"},{"adult":false,"gender":2,"id":1219052,"known_for_department":"Directing","name":"Wes Archer","original_name":"Wes Archer","popularity":1.193,"profile_path":"/rd9MTldbSETmHrgxq7WdTHFaosq.jpg","credit_id":"63156cd30c125500923ad02a","department":"Directing","job":"Other"},{"adult":false,"gender":0,"id":2078913,"known_for_department":"Directing","name":"Jason Boesch","original_name":"Jason Boesch","popularity":0.694,"profile_path":null,"credit_id":"631574f1a402c1007abfee8f","department":"Art","job":"Assistant Art Director"},{"adult":false,"gender":0,"id":3688743,"known_for_department":"Visual Effects","name":"Elisa Phillips","original_name":"Elisa Phillips","popularity":0.6,"profile_path":null,"credit_id":"631575160cb335007a92bdd8","department":"Visual Effects","job":"Lead Character Designer"},{"adult":false,"gender":2,"id":1460507,"known_for_department":"Art","name":"Rafael Hurtado","original_name":"Rafael Hurtado","popularity":0.6,"profile_path":null,"credit_id":"6315754c71083a00801ef5a5","department":"Art","job":"Other"},{"adult":false,"gender":0,"id":1578611,"known_for_department":"Production","name":"Deirdre Brenner","original_name":"Deirdre Brenner","popularity":0.6,"profile_path":null,"credit_id":"63157582fab3fa007c866312","department":"Production","job":"Producer"},{"adult":false,"gender":1,"id":3465199,"known_for_department":"Production","name":"Suzanne St. Clair","original_name":"Suzanne St. Clair","popularity":0.6,"profile_path":null,"credit_id":"63157597fab3fa007f3def7d","department":"Production","job":"Co-Producer"},{"adult":false,"gender":0,"id":1477454,"known_for_department":"Editing","name":"Anthony Alfonso","original_name":"Anthony Alfonso","popularity":0.6,"profile_path":null,"credit_id":"631576139408ec0083e20d2d","department":"Production","job":"Production Manager"},{"adult":false,"gender":2,"id":3688748,"known_for_department":"Production","name":"Jennifer Vogan","original_name":"Jennifer Vogan","popularity":0.6,"profile_path":null,"credit_id":"631576280c1255007ed44d2d","department":"Production","job":"Assistant Production Manager"},{"adult":false,"gender":0,"id":3688762,"known_for_department":"Production","name":"Stephanie Troncoso","original_name":"Stephanie Troncoso","popularity":0.6,"profile_path":null,"credit_id":"631577a5ba131b0081d161c5","department":"Production","job":"Production Coordinator"},{"adult":false,"gender":0,"id":1637782,"known_for_department":"Acting","name":"Katelyn Semer","original_name":"Katelyn Semer","popularity":0.84,"profile_path":null,"credit_id":"6315780d9408ec0080c60b2c","department":"Production","job":"Casting Assistant"},{"adult":false,"gender":2,"id":3688768,"known_for_department":"Sound","name":"Ricardo Watson","original_name":"Ricardo Watson","popularity":0.6,"profile_path":null,"credit_id":"6315789bd40d4c007ab0de48","department":"Sound","job":"Dialogue Editor"},{"adult":false,"gender":1,"id":1099681,"known_for_department":"Production","name":"Tina Chow","original_name":"Tina Chow","popularity":0.828,"profile_path":null,"credit_id":"631578c7e63571007de09d2f","department":"Production","job":"Executive Producer"},{"adult":false,"gender":2,"id":3688776,"known_for_department":"Directing","name":"Nathan Litz","original_name":"Nathan Litz","popularity":0.6,"profile_path":null,"credit_id":"63157a179408ec0080c60bb0","department":"Directing","job":"Series Director"},{"adult":false,"gender":0,"id":3688777,"known_for_department":"Visual Effects","name":"Johnny Tesoro","original_name":"Johnny Tesoro","popularity":0.6,"profile_path":null,"credit_id":"63157a3c1d3143007a43449f","department":"Visual Effects","job":"Supervising Animation Director"},{"adult":false,"gender":2,"id":1998227,"known_for_department":"Visual Effects","name":"Matthew Enright","original_name":"Matthew Enright","popularity":0.98,"profile_path":null,"credit_id":"63157aa31d3143007dcf8b74","department":"Production","job":"Production Manager"},{"adult":false,"gender":0,"id":1405582,"known_for_department":"Sound","name":"Hunter Curra","original_name":"Hunter Curra","popularity":0.6,"profile_path":null,"credit_id":"63157c160cb335007a92bfcb","department":"Sound","job":"Supervising Sound Editor"},{"adult":false,"gender":2,"id":3688782,"known_for_department":"Sound","name":"Corbin Bumeter","original_name":"Corbin Bumeter","popularity":0.6,"profile_path":null,"credit_id":"63157c59bb105700833b2ce3","department":"Sound","job":"Sound Effects Editor"},{"adult":false,"gender":2,"id":3688768,"known_for_department":"Sound","name":"Ricardo Watson","original_name":"Ricardo Watson","popularity":0.6,"profile_path":null,"credit_id":"63157c901d3143007a434561","department":"Sound","job":"ADR Recording Engineer"},{"adult":false,"gender":0,"id":3688784,"known_for_department":"Production","name":"Trymaine Thomas","original_name":"Trymaine Thomas","popularity":0.6,"profile_path":null,"credit_id":"63157caf5507e9007ac16f00","department":"Production","job":"Production Accountant"},{"adult":false,"gender":1,"id":2098123,"known_for_department":"Production","name":"Cynthia Turkington","original_name":"Cynthia Turkington","popularity":0.6,"profile_path":null,"credit_id":"63157cbb0284200080774b2f","department":"Production","job":"Production Accountant"},{"adult":false,"gender":2,"id":3688794,"known_for_department":"Production","name":"Kevin Nodel","original_name":"Kevin Nodel","popularity":0.6,"profile_path":null,"credit_id":"63157d5ac048a9007d102118","department":"Production","job":"Executive Producer's Assistant"},{"adult":false,"gender":2,"id":2478163,"known_for_department":"Production","name":"Jonathan Roig","original_name":"Jonathan Roig","popularity":0.731,"profile_path":null,"credit_id":"63157d6b7fcab3007fbf57a1","department":"Production","job":"Producer"},{"adult":false,"gender":0,"id":3719770,"known_for_department":"Writing","name":"Michael Kellner","original_name":"Michael Kellner","popularity":0.6,"profile_path":null,"credit_id":"6334da8a0f2fbd0081a333ab","department":"Writing","job":"Writers' Assistant"},{"adult":false,"gender":2,"id":209177,"known_for_department":"Writing","name":"Scott Marder","original_name":"Scott Marder","popularity":2.074,"profile_path":null,"credit_id":"60d01a7c0929f6002114d104","department":"Production","job":"Executive Producer"},{"adult":false,"gender":0,"id":57195,"known_for_department":"Directing","name":"Rob Schrab","original_name":"Rob Schrab","popularity":1.388,"profile_path":null,"credit_id":"60d01ab623851300289740ad","department":"Production","job":"Co-Executive Producer"},{"adult":false,"gender":0,"id":2063550,"known_for_department":"Acting","name":"Anne Lane","original_name":"Anne Lane","popularity":1.22,"profile_path":null,"credit_id":"60d01b062ea6b9002c6ce72f","department":"Production","job":"Producer"},{"adult":false,"gender":0,"id":1869299,"known_for_department":"Production","name":"Sydney Ryan","original_name":"Sydney Ryan","popularity":1.4,"profile_path":null,"credit_id":"60d01cec54a8ac00457661f7","department":"Production","job":"Producer"},{"adult":false,"gender":0,"id":2156862,"known_for_department":"Editing","name":"Lee Harting","original_name":"Lee Harting","popularity":0.608,"profile_path":null,"credit_id":"60d0229bcee481007dc9671c","department":"Production","job":"Producer"},{"adult":false,"gender":0,"id":1219678,"known_for_department":"Production","name":"Ollie Green","original_name":"Ollie Green","popularity":0.828,"profile_path":null,"credit_id":"60d023483e2ec800466278c6","department":"Production","job":"Executive Producer"},{"adult":false,"gender":2,"id":1887237,"known_for_department":"Production","name":"Walter Newman","original_name":"Walter Newman","popularity":0.652,"profile_path":null,"credit_id":"60d0235654a8ac00457685b9","department":"Production","job":"Executive Producer"},{"adult":false,"gender":1,"id":3688772,"known_for_department":"Production","name":"Emily Deere","original_name":"Emily Deere","popularity":0.6,"profile_path":null,"credit_id":"631579d30c125500923ad3bd","department":"Production","job":"Line Producer"},{"adult":false,"gender":2,"id":1402559,"known_for_department":"Sound","name":"James A. Moore","original_name":"James A. Moore","popularity":0.6,"profile_path":null,"credit_id":"63157affc048a9008504fa92","department":"Sound","job":"Sound Re-Recording Mixer"},{"adult":false,"gender":1,"id":3606428,"known_for_department":"Production","name":"Kirstin Moser","original_name":"Kirstin Moser","popularity":0.6,"profile_path":null,"credit_id":"63157ca30c1255007ed44f1b","department":"Production","job":"Production Accountant"},{"adult":false,"gender":2,"id":3688792,"known_for_department":"Production","name":"Colton Dempsey","original_name":"Colton Dempsey","popularity":0.6,"profile_path":null,"credit_id":"63157d520d2944007e852170","department":"Production","job":"Executive Producer's Assistant"},{"adult":false,"gender":2,"id":3719769,"known_for_department":"Writing","name":"Jeremy Gilfor","original_name":"Jeremy Gilfor","popularity":0.6,"profile_path":null,"credit_id":"6334da30fa78cd0084b55496","department":"Writing","job":"Writers' Assistant"},{"adult":false,"gender":2,"id":1651036,"known_for_department":"Sound","name":"Ryan Elder","original_name":"Ryan Elder","popularity":0.728,"profile_path":null,"credit_id":"5dcdc5a6b76cbb00117533a2","department":"Sound","job":"Main Title Theme Composer"},{"adult":false,"gender":2,"id":3688771,"known_for_department":"Production","name":"Richard Grieve","original_name":"Richard Grieve","popularity":0.6,"profile_path":null,"credit_id":"6315799ec048a9007d101ff1","department":"Production","job":"Executive Producer"},{"adult":false,"gender":0,"id":1532410,"known_for_department":"Production","name":"Robert McGee","original_name":"Robert McGee","popularity":1.262,"profile_path":null,"credit_id":"631572d31511aa007ba45d81","department":"Production","job":"Casting"},{"adult":false,"gender":1,"id":7902,"known_for_department":"Production","name":"Ruth Lambert","original_name":"Ruth Lambert","popularity":1.303,"profile_path":null,"credit_id":"631573f31d3143007a434314","department":"Production","job":"Casting"},{"adult":false,"gender":2,"id":1309021,"known_for_department":"Production","name":"James A. Fino","original_name":"James A. Fino","popularity":0.766,"profile_path":null,"credit_id":"5347f5ebc3a3686723001478","department":"Production","job":"Executive Producer"},{"adult":false,"gender":2,"id":1245733,"known_for_department":"Acting","name":"Justin Roiland","original_name":"Justin Roiland","popularity":21.112,"profile_path":"/wYApP38aXe6ZcEtlBAfNRxJTQQi.jpg","credit_id":"5347f5cfc3a368672d001405","department":"Production","job":"Executive Producer"},{"adult":false,"gender":0,"id":1278671,"known_for_department":"Visual Effects","name":"Joe Russo","original_name":"Joe Russo","popularity":0.828,"profile_path":null,"credit_id":"5347f626c3a368672b001464","department":"Production","job":"Executive Producer"},{"adult":false,"gender":2,"id":57194,"known_for_department":"Writing","name":"Dan Harmon","original_name":"Dan Harmon","popularity":7.392,"profile_path":"/gDwFosoyPTd0pmnKParzGj3kaMg.jpg","credit_id":"5347f5bfc3a368671c00144a","department":"Production","job":"Executive Producer"},{"adult":false,"gender":2,"id":1216675,"known_for_department":"Production","name":"J. Michael Mendel","original_name":"J. Michael Mendel","popularity":0.681,"profile_path":null,"credit_id":"5bccb090c3a368286600e0c8","department":"Production","job":"Producer"},{"adult":false,"gender":2,"id":1651036,"known_for_department":"Sound","name":"Ryan Elder","original_name":"Ryan Elder","popularity":0.728,"profile_path":null,"credit_id":"5bccba3a925141612600d038","department":"Sound","job":"Original Music Composer"},{"adult":false,"gender":2,"id":1215656,"known_for_department":"Production","name":"Keith Crofford","original_name":"Keith Crofford","popularity":0.732,"profile_path":"/vyNDOCtfryXLFDSvAv5iMXiQuTD.jpg","credit_id":"6063882ca5d849002840e703","department":"Production","job":"Executive Producer"}]}} \ No newline at end of file diff --git a/test_data/https___api_themoviedb_org_3_tv_60625_season_6_episode_1_api_key_19890604_language_zh_CN_append_to_response_external_ids b/test_data/https___api_themoviedb_org_3_tv_60625_season_6_episode_1_api_key_19890604_language_zh_CN_append_to_response_external_ids new file mode 100644 index 00000000..96ef4001 --- /dev/null +++ b/test_data/https___api_themoviedb_org_3_tv_60625_season_6_episode_1_api_key_19890604_language_zh_CN_append_to_response_external_ids @@ -0,0 +1 @@ +{"air_date":"2022-09-04","crew":[{"job":"Editor","department":"Editing","credit_id":"5bccbadb0e0a26016400e0b7","adult":false,"gender":0,"id":2156862,"known_for_department":"Editing","name":"Lee Harting","original_name":"Lee Harting","popularity":0.608,"profile_path":null},{"job":"Art Direction","department":"Art","credit_id":"5bccc8fc0e0a2601620105ff","adult":false,"gender":0,"id":2156888,"known_for_department":"Art","name":"James McDermott","original_name":"James McDermott","popularity":0.84,"profile_path":null},{"job":"Director","department":"Directing","credit_id":"5dd3ce6a28723c00144dc34c","adult":false,"gender":2,"id":117660,"known_for_department":"Directing","name":"Jacob Hair","original_name":"Jacob Hair","popularity":1.176,"profile_path":null},{"job":"Writer","department":"Writing","credit_id":"5ec215d23d3557001ce3b8cc","adult":false,"gender":2,"id":2643127,"known_for_department":"Writing","name":"Albro Lundy IV","original_name":"Albro Lundy IV","popularity":1.176,"profile_path":null},{"job":"In Memory Of","department":"Crew","credit_id":"5ee9f51e1685da003629b87e","adult":false,"gender":2,"id":1216675,"known_for_department":"Production","name":"J. Michael Mendel","original_name":"J. Michael Mendel","popularity":0.681,"profile_path":null},{"job":"Storyboard Artist","department":"Art","credit_id":"631576550c125500923ad2b8","adult":false,"gender":0,"id":3688749,"known_for_department":"Art","name":"Eugene Huang","original_name":"Eugene Huang","popularity":0.6,"profile_path":null},{"job":"Storyboard Artist","department":"Art","credit_id":"6315765db3e627007e369c9d","adult":false,"gender":0,"id":1345328,"known_for_department":"Directing","name":"Juan Jose Meza-Leon","original_name":"Juan Jose Meza-Leon","popularity":1.388,"profile_path":null},{"job":"Storyboard Artist","department":"Art","credit_id":"631576667fcab3007a2198b7","adult":false,"gender":0,"id":3688750,"known_for_department":"Art","name":"Dik Pose","original_name":"Dik Pose","popularity":0.6,"profile_path":null},{"job":"Storyboard Artist","department":"Art","credit_id":"63157676a402c1007f221206","adult":false,"gender":2,"id":3688751,"known_for_department":"Art","name":"Jason D. Warnesky","original_name":"Jason D. Warnesky","popularity":0.6,"profile_path":null},{"job":"Additional Storyboarding","department":"Art","credit_id":"63157683a402c100822ff885","adult":false,"gender":0,"id":3688752,"known_for_department":"Art","name":"Tiana Aguado","original_name":"Tiana Aguado","popularity":0.6,"profile_path":null},{"job":"Additional Storyboarding","department":"Art","credit_id":"6315768bbb1057007b7dee04","adult":false,"gender":1,"id":2149758,"known_for_department":"Art","name":"Shawna Mills","original_name":"Shawna Mills","popularity":0.694,"profile_path":null},{"job":"Editor","department":"Editing","credit_id":"631578224a52f8007e440fd9","adult":false,"gender":0,"id":1815792,"known_for_department":"Editing","name":"Nick Reczynski","original_name":"Nick Reczynski","popularity":1.128,"profile_path":null},{"job":"Assistant Editor","department":"Editing","credit_id":"63157831f9475b007b28d350","adult":false,"gender":0,"id":3688765,"known_for_department":"Editing","name":"Jackson Sost","original_name":"Jackson Sost","popularity":0.6,"profile_path":null},{"job":"Assistant Editor","department":"Editing","credit_id":"63157841f1b5710082c9cade","adult":false,"gender":0,"id":2072621,"known_for_department":"Editing","name":"Valerie Summey","original_name":"Valerie Summey","popularity":0.6,"profile_path":null},{"job":"Animation Supervisor","department":"Visual Effects","credit_id":"631ec8bd9c24fc007d778ac4","adult":false,"gender":2,"id":3688763,"known_for_department":"Visual Effects","name":"Michael Pacheco","original_name":"Michael Pacheco","popularity":0.6,"profile_path":null},{"job":"Animation Supervisor","department":"Visual Effects","credit_id":"631ec8c5435011008286c61e","adult":false,"gender":2,"id":3688778,"known_for_department":"Visual Effects","name":"Miguel Otalora","original_name":"Miguel Otalora","popularity":0.6,"profile_path":null}],"episode_number":1,"guest_stars":[{"character":"Diane Sanchez (voice)","credit_id":"6317f1ce8d77c4007bca60e8","order":801,"adult":false,"gender":1,"id":116315,"known_for_department":"Acting","name":"Kari Wahlgren","original_name":"Kari Wahlgren","popularity":7.97,"profile_path":"/9phhl0oubAKt8D50xLGAb81KPSb.jpg"},{"character":"","credit_id":"6317f1d2d2b209007c70c480","order":802,"adult":false,"gender":2,"id":1365425,"known_for_department":"Acting","name":"Ryan Ridley","original_name":"Ryan Ridley","popularity":3.675,"profile_path":"/mSFssRpQl6ax2QrYTxBBfluVgy2.jpg"},{"character":"","credit_id":"6317f1d61d1bf400797e2cbc","order":803,"adult":false,"gender":1,"id":180986,"known_for_department":"Acting","name":"Cassie Steele","original_name":"Cassie Steele","popularity":10.1,"profile_path":"/2QEVPBwgpKr2Iu7SXR6ywjNqn0x.jpg"}],"name":"索拉瑞克斯星","overview":"史密斯一家在处理上一季的后果,而瑞克和莫蒂被困在太空中。","id":3891529,"production_code":"","runtime":23,"season_number":6,"still_path":"/5tiOEjp03nvaGiKT73knretU8e8.jpg","vote_average":8.3,"vote_count":22,"external_ids":{"imdb_id":"tt21599650","freebase_mid":null,"freebase_id":null,"tvdb_id":9303185,"tvrage_id":null,"wikidata_id":null}} \ No newline at end of file diff --git a/test_data/https___movie_douban_com_subject_35597581_ b/test_data/https___movie_douban_com_subject_35597581_ new file mode 100644 index 00000000..650b7a33 --- /dev/null +++ b/test_data/https___movie_douban_com_subject_35597581_ @@ -0,0 +1,3098 @@ + + + + + + + + + 瑞克和莫蒂 第六季 (豆瓣) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + +
+

+ 瑞克和莫蒂 第六季 Rick and Morty Season 6 + (2022) +

+ +
+ + + +
+ + + + + + + + + +
+
+
+ + + + + + + + + + +
+ 导演: 贾斯汀·罗兰
+ 编剧: 丹·哈萌 / 贾斯汀·罗兰
+ 主演: 贾斯汀·罗兰 / 汤姆·肯尼 / 塔伊加·维迪提 / 克里斯·帕内尔 / 山姆·尼尔 / 保罗·吉亚玛提 / 凯瑟琳·特纳 / 马修·布罗德里克 / 利亚姆·坎宁安 / 斯宾瑟·格拉默 / 萨拉·乔克
+ 类型: 喜剧 / 科幻 / 动画 / 冒险
+ 官方网站: https://www.rickandmorty.com
+ 制片国家/地区: 美国
+ 语言: 英语
+ 首播: 2022-09-04(美国)
+ 季数:
+ 集数: 10
+ 单集片长: 22分钟
+ + IMDb: tt15389382
+ +
+ + + + +
+ + + + +
+
+
+ + + + +
+ + + + +
+ 9.5 + + +
+
+ +
+ + + 5星 + +
+ 80.7% +
+
+
+ + + 4星 + +
+ 14.6% +
+
+
+ + + 3星 + +
+ 3.5% +
+
+
+ + + 2星 + +
+ 0.6% +
+
+
+ + + 1星 + +
+ 0.5% +
+
+
+ +
+
+ + + +
+ + + + + + + +
+ + + + +
+ + + 评价: + + + + + + + + + + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + +
+ + + +
+ + + + + + + + + +

+ 瑞克和莫蒂 第六季的分集短评 + · · · · · · +

+ + + + + +
+ + + + + 1集 + + + 2集 + + + 3集 + + + 4集 + + + 5集 + + + 6集 + + + 7集 + + + 8集 + + + 9集 + + + 10集 + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +

+ 小组讨论 +   ·  ·  ·  ·  ·  · +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
这集让我好难过来自骆斐9 回应2022-12-16 07:25:55
哭了来自momo10 回应2022-12-15 13:26:51
瑞莫宇宙里好像第一次出现比rick能力强的存在 恐龙来自醒醒😶‍🌫️9 回应2022-10-14 19:56:39
有谁能完整找出里面有哪些明星不😂?来自阿那伊麻丝2 回应2022-12-15 02:56:05
刘慈欣很早就写过关于恐龙的科幻小说来自颜良7 回应2022-12-14 19:53:21
虎头蛇尾来自豆友26042811004 回应2022-12-14 17:59:15
大胆开麦s6e8屁股本身就很歪来自Helerramar65 回应2022-12-14 16:26:35
“自私和无私其实是一体两面”来自秦奋15 回应2022-12-14 14:43:50
”You failed me”. “ No. I failed you”来自从3D世界飘过14 回应2022-12-14 04:16:55
谁知道最后姥爷和莫蒂看计划大屏时的背景乐来自fan1 回应2022-12-14 00:34:10
+ + +

+ + > 去这部剧集的小组讨论(全部3122条) + +

+
+ + + + + + + + + + + + + + + + + + + + + +
+ + +

+ 喜欢这部剧集的人也喜欢 + · · · · · · +

+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + +

+ 瑞克和莫蒂 第六季的短评 + · · · · · · + + ( + 全部 3919 条 + ) + +

+ +
+ + + + +
+ +
+ 热门 /  + 最新 /  + +
+ +
+
+ + + + +
+ + +
+

+ + 36 + + + + + + + + 王俊俊 + 看过 + + + 2022-09-06 19:11:24 + + 上海 + +

+

+ + 第一集我看到了主创的绝望。如果解构虚无和人情矛盾都成为了吃老本泡沫剧却还要摆出自以为是的深刻,那就足以说明他们有在进行反思,却没有那份才华。离开了制片和几个金牌编剧之后,导演的真实水平就暴露了,绝望也就随之而下。当然,这绝望只是第一集给我的感觉,后面的毕竟还没出(希望可以有分集评分更准确),按照以前我肯定会去外网查所有编剧资料,但现在已经丝毫没兴致了。回升或是烂俗到底,都祝愿可以发财赚钱,出到10... + 第一集我看到了主创的绝望。如果解构虚无和人情矛盾都成为了吃老本泡沫剧却还要摆出自以为是的深刻,那就足以说明他们有在进行反思,却没有那份才华。离开了制片和几个金牌编剧之后,导演的真实水平就暴露了,绝望也就随之而下。当然,这绝望只是第一集给我的感觉,后面的毕竟还没出(希望可以有分集评分更准确),按照以前我肯定会去外网查所有编剧资料,但现在已经丝毫没兴致了。回升或是烂俗到底,都祝愿可以发财赚钱,出到100季,瑞克莫蒂100年。 + (展开) +

+
+
+ + +
+ +
+ + +
+

+ + 809 + + + + + + + + paradise + 看过 + + + 2022-09-04 11:59:58 + + 美国 + +

+

+ + 本来还在感叹《风骚律师》的完结让活着的理由又少了一个,但是《瑞克与莫蒂》在此时播出了 +

+
+
+ + +
+ +
+ + +
+

+ + 572 + + + + + + + + 星际牛奶🥛 + 看过 + + + 2022-09-05 14:32:05 + + 黑龙江 + +

+

+ + 一家人整整齐齐的埋尸体真的笑鼠,家庭团建活动。 +

+
+
+ + +
+ +
+ + +
+

+ + 83 + + + + + + + + 少年赵里昂 + 看过 + + + 2022-09-05 15:33:56 + + 陕西 + +

+

+ + 我愿称第一集为“不忘初心”。另外第一集就引入一个boss不知道是不是意味着整季要更环环相扣了,但也会担心反而为了故事完整性而牺牲很多有趣的脑洞。 +

+
+
+ + +
+ +
+ + +
+

+ + 43 + + + + + + + + 水仙操 + 看过 + + + 2022-09-05 18:00:26 + + + +

+

+ + Diane +

+
+
+ + +
+ + + + + + > + 更多短评 + 3919条 + +
+
+
+
+
+ 为什么被折叠? + 有一些短评被折叠了 +
+ 评论被折叠,是因为发布这条评论的帐号行为异常。评论仍可以被展开阅读,对发布人的账号不造成其他影响。如果认为有问题,可以联系豆瓣电影。 +
+
+
+
+ +
+
+ + + + + + +
+ 你关注的人还没写过短评 +
+ +
+
+ + + + + + +
+
+ + + + + + + + + +
+
+ + + 我要写剧评 + +

+ 瑞克和莫蒂 第六季的剧评 · · · · · · + + ( 全部 22 条 ) +

+
+ +
+ 热门 / + 最新 / + 好友 +
+ + + + + + + +
+ + + + + +
+
+ + + +
+ + + + + 一只老喵 + + + + 2022-09-08 00:43:11 + + +
+ + +
+ +

“人”才是矛盾的统一体

+ +
+
+ + “人”才是矛盾的统一体,神不是。 我们的主角瑞克虽然一直声称“I am the god”,但是其实没有人比他自己更清楚自己其实就是一个“普通”的Rick。 自从第五季最后一集我们知道了,瑞克其实并不一定会是宇宙里最聪明的人,也知道了有一个比自称“the Rickest Rick”更牛逼的九... + +  (展开) +
+
+ + + + +
+
+
+ + + +
+
+ + + +
+ + + + + 478 + + + 2022-09-08 01:48:48 + + +
+ + +
+ +

虚假的虚无主义者

+ +
+
+ + 第二集更新: 十分之拉胯 ﹉﹉﹉﹉﹉﹉﹉﹉﹉﹉ s6-1是最好的一集。此剧卖点向来是大量脑洞,但后几季剧情的趋势是脑洞内容减少,然后“无所谓”的主角随意玩梗,偶尔加点小感想,再玩些20禁内容,给年轻观众一种“我什么都无所谓,我比神还强”的无敌感,让一些观众感觉很爽。... + +  (展开) +
+
+ + + + +
+
+
+ + + +
+
+ + + +
+ + + + + 发条巧克力 + + + + 2022-09-07 12:23:06 + + +
+ + +
+ +

为什么说第一集让一个老粉恢复了信心?

+ +
+
+

这篇剧评可能有剧透

+ + 期待已久的瑞莫第六季终于开播。但是在第一集开播以后,我看到很多人说无聊、水甚至有人说烂尾了。但是对于我这个粉丝来说,第一集让我恢复了第五季一度失去的信心。我相信剧组也能够越做越好。 一、let go and move on 本集很有意思的一个设计是把柯南伯格宇宙的Jerry(也就是... + +  (展开) +
+
+ + + + +
+
+
+ + + +
+
+ + + +
+ + + + + 西班牙馅饼 + + + + 2022-09-13 10:56:38 + + +
+ + +
+ +

E2.第二集中的宗教隐喻

+ +
+
+

这篇剧评可能有剧透

+ + 虽然Rick在这一集里反复强调“这不是宗教”,但我们不难从本集中看出一些对于现实宗教的反映。 一、 游戏中的每个人都是来自于同样的东西,有着共同的归属——Morty。 然而,人性是复杂的,虽然游戏中的人物都同样来自于Morty的人格碎片,但却分成了各种各样的派别。 反观现实... + +  (展开) +
+
+ + + + +
+
+
+ + + +
+
+ + + +
+ + + + + 大风哥Wind + + + + 2022-09-05 23:45:38 + + +
+ + +
+ +

一家人就要整整齐齐,跟瑞克和莫蒂get地道口语

+ +
+
+ + 九月真是神剧打架的月份,开篇调侃屎大颗的梗有笑到~除了一如既往的脑洞,台词也一如既往地适合学口语。我一边看剧一边总结了这些实用口语表达分享给你—— hash sth out 协商达成共识 hash sth out 和其他人协商达成共识,这个短语很可能源自hash over 不断地讨论。在关键时刻... + +  (展开) +
+
+ + + + +
+
+
+ + + +
+
+ + + +
+ + + + + weber ann + + + + 2022-09-09 21:48:27 + + +
+ + +
+ +

只关于S06E01的碎碎念 如果连死亡都无法终结这一切,那么一直沉溺于痛苦我其实会不会好些

+ +
+
+

这篇剧评可能有剧透

+ + 马男季终的时候,我感觉自己像经历了一场大型失恋。看着最后老马跟黛安沉默地望着夜空,我没忍住直接号啕大哭,因为我知道我应该再难有这样一个青春陪伴始终,同我一起成长,每一颗眼泪都懂得,每一份心酸都明白的伙伴了。马男于我而言早就不是一部剧那么简单了,他已经变成我... + +  (展开) +
+
+ + + + +
+
+
+ + + +
+
+ + + +
+ + + + + 安琦 + + + 2022-09-27 17:36:43 + + +
+ + +
+ +

S6E4封神,以前的感觉又回来了

+ +
+
+ + 第四集神了,通过一个设备,直接把白天的自己和夜晚的自己分成了两个世界的人。白天的阶级掌握主动权去强迫夜晚的阶级做自己不想要做的事情,然后享受其成果。这不就是资本主义和旧的集权统治社会吗。然后下层人民希望上层人民能够聆听自己的需求,分担一些。但是这个制度就是... + +  (展开) +
+
+ + + + +
+
+
+ + + +
+
+ + + +
+ + + + + AcyclicT + + + 2022-09-07 11:41:29 + + +
+ + +
+ +

我不敢相信我居然不能每一集写一个短评所以我只能写一个长评?(每一句可能都是剧透

+ +
+
+

这篇剧评可能有剧透

+ + 首先我想讲一个个人好恶。那就是我非常讨厌那种很轻易说出的 “这季不如以前了” 类似的话。原因主要是太多不怎么动脑的人会不假思索地说这句话,导致这句话本身(哪怕它是个天才说的,而且100%正确),在我眼里也充斥着媚俗和恶心。所以这些年来我即使真的想表达这个意思,一... + +  (展开) +
+
+ + + + +
+
+
+ + + +
+
+ + + +
+ + + + + Tuzki + + + + 2022-09-06 11:01:45 + + +
+ + +
+ +

我七七八八的胡思乱想

+ +
+
+

这篇剧评可能有剧透

+ + 草(一种植物)!一家五口人,有三个都来自他们各自不同的宇宙并代替了当前宇宙的自己,这岂止是道德崩坏!主角瑞克因为妻女被杀手瑞克杀害来到了主角莫蒂的宇宙;又在第一季瑞克和莫蒂两人意外毁灭世界,两人便抛下了家人逃去其它宇宙;而可怜的杰瑞在“杰瑞托儿所”果然被交... + +  (展开) +
+
+ + + + +
+
+
+ + + +
+
+ + + +
+ + + + + 火山 + + + + 2022-09-25 23:44:01 + + +
+ + +
+ +

e03——从姥爷锁进酒柜的金星酒说起

+ +
+
+

这篇剧评可能有剧透

+ + 金星,希腊语 阿佛洛狄忒,罗马名 Venus,即是金星,也是脍炙人口的维纳斯,爱、美和性欲之神。 在将太空 Beth 带来的金星酒藏进酒柜之后,姥爷直接把酒柜的遥控器扔进了粉碎机。如果像很多人猜测的那样,从一开始就是姥爷在酒里动了手脚,这个举动显得不合情理。 虽然不知道为... + +  (展开) +
+
+ + + + +
+
+
+ + + + + + + + + + +
+ + + + + + + + +

+ > + + 更多剧评 + 22篇 + +

+
+ + + + + +
+ + + + + + + +
+
+ + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + +
+
+
+ + + + + + + + + + +
+ + +

+ 以下片单推荐 + · · · · · · + + ( + 全部 + ) + +

+ + + + + +
+ + + + + + + + + + + + +
+ + +

+ 谁在看这部剧集 + · · · · · · +

+ + + + + +
+ + 10050人在看 +  /  + 14886人看过 +  /  + 23104人想看 +
+ +
+ + + + + + +
+ + + + + +
+ + +

订阅瑞克和莫蒂 第六季的影评:
+ feed: rss 2.0

+ + +
+
+ + + +
+ + + + +
+ + +
+
+
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +