diff --git a/boofilsic/settings.py b/boofilsic/settings.py
index 086bac28..d5d9f90e 100644
--- a/boofilsic/settings.py
+++ b/boofilsic/settings.py
@@ -13,7 +13,6 @@ https://docs.djangoproject.com/en/3.0/ref/settings/
import os
import psycopg2.extensions
-
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@@ -29,6 +28,11 @@ DEBUG = True
ALLOWED_HOSTS = ['*']
+# To allow debug in template context
+# https://docs.djangoproject.com/en/3.1/ref/settings/#internal-ips
+INTERNAL_IPS = [
+ "127.0.0.1"
+]
# Application definition
@@ -39,10 +43,18 @@ INSTALLED_APPS = [
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
+ 'django.contrib.humanize',
+ 'django.contrib.postgres',
'markdownx',
- 'users.apps.UsersConfig',
+ 'management.apps.ManagementConfig',
+ 'mastodon.apps.MastodonConfig',
'common.apps.CommonConfig',
+ 'users.apps.UsersConfig',
'books.apps.BooksConfig',
+ 'movies.apps.MoviesConfig',
+ 'music.apps.MusicConfig',
+ 'games.apps.GamesConfig',
+ 'easy_thumbnails',
]
MIDDLEWARE = [
@@ -86,7 +98,7 @@ if DEBUG:
'NAME': 'test',
'USER': 'donotban',
'PASSWORD': 'donotbansilvousplait',
- 'HOST': '172.18.47.7',
+ 'HOST': '172.18.116.29',
'OPTIONS': {
'client_encoding': 'UTF8',
# 'isolation_level': psycopg2.extensions.ISOLATION_LEVEL_DEFAULT,
@@ -112,7 +124,7 @@ else:
# https://docs.djangoproject.com/en/3.0/topics/auth/customizing/#authentication-backends
AUTHENTICATION_BACKENDS = [
- 'common.mastodon.auth.OAuth2Backend',
+ 'mastodon.auth.OAuth2Backend',
]
@@ -137,31 +149,30 @@ if not DEBUG:
SECURE_HSTS_PRELOAD = True
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_SECONDS = 31536000
+
LOGGING = {
- 'version': 1,
- 'disable_existing_loggers': False,
- 'formatters': {
- 'simple': {
- 'format': '{levelname} {asctime} {module} {message}',
- 'style': '{',
+ 'version': 1,
+ 'disable_existing_loggers': False,
+ 'formatters': {
+ 'simple': {
+ 'format': '{levelname} {asctime} {name}:{lineno} {message}',
+ 'style': '{',
+ },
+ },
+ 'handlers': {
+ 'file': {
+ 'level': 'INFO',
+ 'class': 'logging.FileHandler',
+ 'filename': os.path.join(BASE_DIR, 'log'),
+ 'formatter': 'simple'
+ },
},
- },
- 'handlers': {
- 'file': {
- 'level': 'INFO',
- 'class': 'logging.FileHandler',
- 'filename': os.path.join(BASE_DIR, 'log'),
- 'formatter': 'simple'
- },
- },
- 'loggers': {
- 'django': {
+ 'root': {
'handlers': ['file'],
'level': 'INFO',
'propagate': True,
},
- },
-}
+ }
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.0/howto/static-files/
@@ -169,22 +180,31 @@ if not DEBUG:
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')
+STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
+
AUTH_USER_MODEL = 'users.User'
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/')
-CLIENT_ID = 'kEbwT9Je5HHg4FoLx4nb0tNaIrPNs5Mw6AYlQlsj2_4'
-CLIENT_SECRET = 'xwmEvlmudLCkBmvdzGf8m41Ug5o5di9xnDqeVLrcKSg'
+# Mastodon configs
+CLIENT_NAME = 'NiceDB'
+APP_WEBSITE = 'https://nicedb.org'
+REDIRECT_URIS = "https://nicedb.org/users/OAuth2_login/\nhttps://www.nicedb.org/users/OAuth2_login/"
-# Path to save report related images, ends without slash
+# Path to save report related images, ends with slash
REPORT_MEDIA_PATH_ROOT = 'report/'
MARKDOWNX_MEDIA_PATH = 'review/'
BOOK_MEDIA_PATH_ROOT = 'book/'
DEFAULT_BOOK_IMAGE = os.path.join(BOOK_MEDIA_PATH_ROOT, 'default.svg')
-
-# Mastodon domain name
-MASTODON_DOMAIN_NAME = 'donotban.com'
+MOVIE_MEDIA_PATH_ROOT = 'movie/'
+DEFAULT_MOVIE_IMAGE = os.path.join(MOVIE_MEDIA_PATH_ROOT, 'default.svg')
+SONG_MEDIA_PATH_ROOT = 'song/'
+DEFAULT_SONG_IMAGE = os.path.join(SONG_MEDIA_PATH_ROOT, 'default.svg')
+ALBUM_MEDIA_PATH_ROOT = 'album/'
+DEFAULT_ALBUM_IMAGE = os.path.join(ALBUM_MEDIA_PATH_ROOT, 'default.svg')
+GAME_MEDIA_PATH_ROOT = 'game/'
+DEFAULT_GAME_IMAGE = os.path.join(GAME_MEDIA_PATH_ROOT, 'default.svg')
# Timeout of requests to Mastodon, in seconds
MASTODON_TIMEOUT = 30
@@ -208,8 +228,31 @@ LOGIN_URL = '/users/login/'
ADMIN_URL = 'lpLuTqX72Bt2hLfxxRYKeTZdE59Y2hLfpLuTqX72Btx9sXuljYK4tYEmjrHd'
# Luminati proxy settings
-LUMINATI_USERNAME = '***REMOVED***'
-LUMINATI_PASSWORD = '***REMOVED***'
+LUMINATI_USERNAME = 'lum-customer-hl_7bed6f85-zone-static'
+LUMINATI_PASSWORD = 'dwy4lz5ck438'
+
+# Spotify credentials
+# SPOTIFY_CLIENT_ID = "***REMOVED***"
+# SPOTIFY_CLIENT_SECRET = "***REMOVED***"
+SPOTIFY_CREDENTIAL = "***REMOVED***"
+
+# IMDb API service https://imdb-api.com/
+IMDB_API_KEY = "***REMOVED***"
+
+# Thumbnail setting
+# It is possible to optimize the image size even more: https://easy-thumbnails.readthedocs.io/en/latest/ref/optimize/
+THUMBNAIL_ALIASES = {
+ '': {
+ 'normal': {
+ 'size': (200, 200),
+ 'crop': 'scale',
+ 'autocrop': True,
+ },
+ },
+}
+# THUMBNAIL_PRESERVE_EXTENSIONS = ('svg',)
+if DEBUG:
+ THUMBNAIL_DEBUG = True
# https://django-debug-toolbar.readthedocs.io/en/latest/
# maybe benchmarking before deployment
diff --git a/boofilsic/urls.py b/boofilsic/urls.py
index 1c9cec13..213c393e 100644
--- a/boofilsic/urls.py
+++ b/boofilsic/urls.py
@@ -26,6 +26,7 @@ urlpatterns = [
path('books/', include('books.urls')),
path('movies/', include('movies.urls')),
path('music/', include('music.urls')),
+ path('games/', include('games.urls')),
path('announcement/', include('management.urls')),
path('', include('common.urls')),
diff --git a/books/templates/books/detail.html b/books/templates/books/detail.html
index 885b27dd..fd8ee1e7 100644
--- a/books/templates/books/detail.html
+++ b/books/templates/books/detail.html
@@ -89,7 +89,7 @@
{% if book.other_info %}
{% for k, v in book.other_info.items %}
- {{k}}:{{v}}
+ {{ k }}:{{ v | urlize }}
{% endfor %}
{% endif %}
diff --git a/books/templates/books/scrape.html b/books/templates/books/scrape.html
index d24537a9..6ad5a8a1 100644
--- a/books/templates/books/scrape.html
+++ b/books/templates/books/scrape.html
@@ -44,7 +44,7 @@
{% trans '解析器:' %}
diff --git a/common/config.py b/common/config.py
new file mode 100644
index 00000000..2f5b69df
--- /dev/null
+++ b/common/config.py
@@ -0,0 +1,20 @@
+# how many items are showed in one search result page
+ITEMS_PER_PAGE = 20
+
+# how many pages links in the pagination
+PAGE_LINK_NUMBER = 7
+
+# max tags on list page
+TAG_NUMBER_ON_LIST = 5
+
+# how many books have in each set at the home page
+BOOKS_PER_SET = 5
+
+# how many movies have in each set at the home page
+MOVIES_PER_SET = 5
+
+# how many music items have in each set at the home page
+MUSIC_PER_SET = 5
+
+# how many games have in each set at the home page
+GAMES_PER_SET = 5
diff --git a/common/models.py b/common/models.py
index cfe802a9..745a7c4d 100644
--- a/common/models.py
+++ b/common/models.py
@@ -22,6 +22,7 @@ class SourceSiteEnum(models.TextChoices):
DOUBAN = "douban", _("豆瓣")
SPOTIFY = "spotify", _("Spotify")
IMDB = "imdb", _("IMDb")
+ STEAM = "steam", _("STEAM")
class Entity(models.Model):
diff --git a/common/scraper.py b/common/scraper.py
index 2c1f323e..37ad1d58 100644
--- a/common/scraper.py
+++ b/common/scraper.py
@@ -22,6 +22,8 @@ from books.models import Book
from books.forms import BookForm
from music.models import Album, Song
from music.forms import AlbumForm, SongForm
+from games.models import Game
+from games.forms import GameForm
RE_NUMBERS = re.compile(r"\d+\d*")
@@ -1018,3 +1020,131 @@ class ImdbMovieScraper(AbstractScraper):
@classmethod
def get_api_url(cls, url):
return f"https://imdb-api.com/zh/API/Title/{IMDB_API_KEY}/{cls.regex.findall(url)[0]}/FullActor,"
+
+
+class DoubanGameScraper(AbstractScraper):
+ site_name = SourceSiteEnum.DOUBAN.value
+ host = 'www.douban.com/game/'
+ data_class = Game
+ form_class = GameForm
+
+ regex = re.compile(r"https://www\.douban\.com/game/\d+/{0,1}")
+
+ def scrape(self, url):
+ headers = DEFAULT_REQUEST_HEADERS.copy()
+ headers['Host'] = 'www.douban.com'
+ content = self.download_page(url, headers)
+
+ try:
+ raw_title = content.xpath(
+ "//div[@id='content']/h1/text()")[0].strip()
+ except IndexError:
+ raise ValueError("given url contains no movie info")
+
+ title = raw_title
+
+ other_title_elem = content.xpath(
+ "//dl[@class='game-attr']//dt[text()='别名:']/following-sibling::dd[1]/text()")
+ other_title = other_title_elem[0].strip().split(' / ') if other_title_elem else None
+
+ developer_elem = content.xpath(
+ "//dl[@class='game-attr']//dt[text()='开发商:']/following-sibling::dd[1]/text()")
+ developer = developer_elem[0].strip().split(' / ') if developer_elem else None
+
+ publisher_elem = content.xpath(
+ "//dl[@class='game-attr']//dt[text()='发行商:']/following-sibling::dd[1]/text()")
+ publisher = publisher_elem[0].strip().split(' / ') if publisher_elem else None
+
+ platform_elem = content.xpath(
+ "//dl[@class='game-attr']//dt[text()='平台:']/following-sibling::dd[1]/a/text()")
+ platform = platform_elem if platform_elem else None
+
+ genre_elem = content.xpath(
+ "//dl[@class='game-attr']//dt[text()='类型:']/following-sibling::dd[1]/a/text()")
+ genre = None
+ if genre_elem:
+ genre = [g for g in genre_elem if g != '游戏']
+
+ date_elem = content.xpath(
+ "//dl[@class='game-attr']//dt[text()='发行日期:']/following-sibling::dd[1]/text()")
+ release_date = dateparser.parse(date_elem[0].strip(), settings={
+ "RELATIVE_BASE": datetime.datetime(1900, 1, 1)}) if date_elem else None
+
+ brief_elem = content.xpath("//div[@class='mod item-desc']/p/text()")
+ brief = '\n'.join(brief_elem) if brief_elem else None
+
+ img_url_elem = content.xpath(
+ "//div[@class='item-subject-info']/div[@class='pic']//img/@src")
+ img_url = img_url_elem[0].strip() if img_url_elem else None
+ raw_img, ext = self.download_image(img_url)
+
+ data = {
+ 'title': title,
+ 'other_title': other_title,
+ 'developer': developer,
+ 'publisher': publisher,
+ 'release_date': release_date,
+ 'genre': genre,
+ 'platform': platform,
+ 'brief': brief,
+ 'other_info': None,
+ 'source_site': self.site_name,
+ 'source_url': self.get_effective_url(url),
+ }
+
+ self.raw_data, self.raw_img, self.img_ext = data, raw_img, ext
+ return data, raw_img
+
+
+class SteamGameScraper(AbstractScraper):
+ site_name = SourceSiteEnum.STEAM.value
+ host = 'store.steampowered.com'
+ data_class = Game
+ form_class = GameForm
+
+ regex = re.compile(r"https://store\.steampowered\.com/app/\d+/{0,1}")
+
+ def scrape(self, url):
+ headers = DEFAULT_REQUEST_HEADERS.copy()
+ headers['Host'] = self.host
+ content = self.download_page(url, headers)
+
+ title = content.xpath("//div[@class='apphub_AppName']/text()")[0]
+ developer = content.xpath("//div[@id='developers_list']/a/text()")
+ publisher = content.xpath("//div[@class='glance_ctn']//div[@class='dev_row'][2]//a/text()")
+ release_date = dateparser.parse(
+ content.xpath(
+ "//div[@class='release_date']/div[@class='date']/text()")[0],
+ settings={
+ "RELATIVE_BASE": datetime.datetime(1900, 1, 1)
+ }
+ )
+
+ genre = content.xpath(
+ "//div[@class='details_block']/b[2]/following-sibling::a/text()")
+
+ platform = ['PC']
+
+ brief = content.xpath(
+ "//div[@class='game_description_snippet']/text()")[0].strip()
+
+ img_url = content.xpath("//img[@class='game_header_image_full']/@src")[
+ 0].replace("header.jpg", "library_600x900.jpg")
+ raw_img, ext = self.download_image(img_url)
+
+ data = {
+ 'title': title,
+ 'other_title': None,
+ 'developer': developer,
+ 'publisher': publisher,
+ 'release_date': release_date,
+ 'genre': genre,
+ 'platform': platform,
+ 'brief': brief,
+ 'other_info': None,
+ 'source_site': self.site_name,
+ 'source_url': self.get_effective_url(url),
+ }
+
+ self.raw_data, self.raw_img, self.img_ext = data, raw_img, ext
+ return data, raw_img
diff --git a/common/static/css/boofilsic.css b/common/static/css/boofilsic.css
index 8623d363..0ddbdc36 100644
--- a/common/static/css/boofilsic.css
+++ b/common/static/css/boofilsic.css
@@ -1246,6 +1246,14 @@ select::placeholder {
font-weight: bold;
}
+.source-label.source-label__steam {
+ background: linear-gradient(30deg, #1387b8, #111d2e);
+ color: white;
+ border: none;
+ font-weight: 600;
+ padding-top: 2px;
+}
+
.main-section-wrapper {
padding: 32px 48px 32px 36px;
background-color: #f7f7f7;
diff --git a/common/static/css/boofilsic.min.css b/common/static/css/boofilsic.min.css
index 53607bef..83a180ca 100644
--- a/common/static/css/boofilsic.min.css
+++ b/common/static/css/boofilsic.min.css
@@ -1 +1 @@
-@import url(https://cdn.jsdelivr.net/npm/skeleton-css@2.0.4/css/normalize.css);.button,button,input[type='button'],input[type='reset'],input[type='submit']{background-color:#00a1cc;border:0.1rem solid #00a1cc;border-radius:.4rem;color:#fff;cursor:pointer;display:inline-block;font-size:1.1rem;font-weight:700;height:3.4rem;letter-spacing:.1rem;line-height:3.4rem;padding:0 2.8rem;text-align:center;text-decoration:none;text-transform:uppercase;white-space:nowrap}.button:focus,.button:hover,button:focus,button:hover,input[type='button']:focus,input[type='button']:hover,input[type='reset']:focus,input[type='reset']:hover,input[type='submit']:focus,input[type='submit']:hover{background-color:#606c76;border-color:#606c76;color:#fff;outline:0}.button[disabled],button[disabled],input[type='button'][disabled],input[type='reset'][disabled],input[type='submit'][disabled]{cursor:default;opacity:.5}.button[disabled]:focus,.button[disabled]:hover,button[disabled]:focus,button[disabled]:hover,input[type='button'][disabled]:focus,input[type='button'][disabled]:hover,input[type='reset'][disabled]:focus,input[type='reset'][disabled]:hover,input[type='submit'][disabled]:focus,input[type='submit'][disabled]:hover{background-color:#00a1cc;border-color:#00a1cc}.button.button-outline,button.button-outline,input[type='button'].button-outline,input[type='reset'].button-outline,input[type='submit'].button-outline{background-color:transparent;color:#00a1cc}.button.button-outline:focus,.button.button-outline:hover,button.button-outline:focus,button.button-outline:hover,input[type='button'].button-outline:focus,input[type='button'].button-outline:hover,input[type='reset'].button-outline:focus,input[type='reset'].button-outline:hover,input[type='submit'].button-outline:focus,input[type='submit'].button-outline:hover{background-color:transparent;border-color:#606c76;color:#606c76}.button.button-outline[disabled]:focus,.button.button-outline[disabled]:hover,button.button-outline[disabled]:focus,button.button-outline[disabled]:hover,input[type='button'].button-outline[disabled]:focus,input[type='button'].button-outline[disabled]:hover,input[type='reset'].button-outline[disabled]:focus,input[type='reset'].button-outline[disabled]:hover,input[type='submit'].button-outline[disabled]:focus,input[type='submit'].button-outline[disabled]:hover{border-color:inherit;color:#00a1cc}.button.button-clear,button.button-clear,input[type='button'].button-clear,input[type='reset'].button-clear,input[type='submit'].button-clear{background-color:transparent;border-color:transparent;color:#00a1cc}.button.button-clear:focus,.button.button-clear:hover,button.button-clear:focus,button.button-clear:hover,input[type='button'].button-clear:focus,input[type='button'].button-clear:hover,input[type='reset'].button-clear:focus,input[type='reset'].button-clear:hover,input[type='submit'].button-clear:focus,input[type='submit'].button-clear:hover{background-color:transparent;border-color:transparent;color:#606c76}.button.button-clear[disabled]:focus,.button.button-clear[disabled]:hover,button.button-clear[disabled]:focus,button.button-clear[disabled]:hover,input[type='button'].button-clear[disabled]:focus,input[type='button'].button-clear[disabled]:hover,input[type='reset'].button-clear[disabled]:focus,input[type='reset'].button-clear[disabled]:hover,input[type='submit'].button-clear[disabled]:focus,input[type='submit'].button-clear[disabled]:hover{color:#00a1cc}select{background:url('data:image/svg+xml;utf8,
') center right no-repeat;padding-right:3.0rem}select:focus{background-image:url('data:image/svg+xml;utf8,
')}textarea{min-height:6.5rem;width:100%}select{width:100%}label,legend{display:block;margin-bottom:.5rem}fieldset{border-width:0;padding:0}input[type='checkbox'],input[type='radio']{display:inline}.label-inline{display:inline-block;font-weight:normal;margin-left:.5rem}dl,ol,ul{list-style:none;margin-top:0;padding-left:0}ol{list-style:decimal inside}ul{list-style:circle inside}.button,button,dd,dt,li{margin-bottom:1.0rem}fieldset,input,select,textarea{margin-bottom:1.5rem}blockquote,dl,figure,form,ol,p,pre,table,ul{margin-bottom:1rem}b,strong{font-weight:bold}p{margin-top:0}h1,h2,h3,h4,h5,h6{font-weight:300;letter-spacing:-.1rem;margin-bottom:2.0rem;margin-top:0}h1{font-size:4.6rem;line-height:1.2}h2{font-size:3.6rem;line-height:1.25}h3{font-size:2.8rem;line-height:1.3}h4{font-size:2.2rem;letter-spacing:-.08rem;line-height:1.35}h5{font-size:1.8rem;letter-spacing:-.05rem;line-height:1.5}h6{font-size:1.6rem;letter-spacing:0;line-height:1.4}img{max-width:100%;-o-object-fit:contain;object-fit:contain}img.emoji{height:14px;-webkit-box-sizing:border-box;box-sizing:border-box;-o-object-fit:contain;object-fit:contain;position:relative;top:3px}img.emoji--large{height:20px;position:relative;top:2px}.clearfix:after{clear:both;content:' ';display:table}.float-left{float:left}.float-right{float:right}.highlight{font-weight:bold}:root{font-size:10px}*,*:after,*:before{-webkit-box-sizing:inherit;box-sizing:inherit}html{-webkit-box-sizing:border-box;box-sizing:border-box;height:100%}body{color:#606c76;font-family:'Roboto', 'Helvetica Neue', 'Helvetica', 'Arial', 'Microsoft YaHei Light', sans-serif;font-size:1.3rem;font-weight:300;letter-spacing:.05rem;line-height:1.6;margin:0;height:100%}textarea{font-family:'Roboto', 'Helvetica Neue', 'Helvetica', 'Arial', 'Microsoft YaHei Light', sans-serif}a{color:#00a1cc;text-decoration:none}a:active,a:hover,a:hover:visited{color:#606c76}li{list-style:none}input[type=text]::-ms-clear,input[type=text]::-ms-reveal{display:none;width:0;height:0}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-results-button,input[type="search"]::-webkit-search-results-decoration{display:none}input[type='email'],input[type='number'],input[type='password'],input[type='search'],input[type='tel'],input[type='text'],input[type='url'],input[type='date'],input[type='time'],input[type='color'],textarea,select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:transparent;border:0.1rem solid #ccc;border-radius:.4rem;-webkit-box-shadow:none;box-shadow:none;-webkit-box-sizing:inherit;box-sizing:inherit;padding:.6rem 1.0rem}input[type='email']:focus,input[type='number']:focus,input[type='password']:focus,input[type='search']:focus,input[type='tel']:focus,input[type='text']:focus,input[type='url']:focus,input[type='date']:focus,input[type='time']:focus,input[type='color']:focus,textarea:focus,select:focus{border-color:#00a1cc;outline:0}input[type='email']::-webkit-input-placeholder,input[type='number']::-webkit-input-placeholder,input[type='password']::-webkit-input-placeholder,input[type='search']::-webkit-input-placeholder,input[type='tel']::-webkit-input-placeholder,input[type='text']::-webkit-input-placeholder,input[type='url']::-webkit-input-placeholder,input[type='date']::-webkit-input-placeholder,input[type='time']::-webkit-input-placeholder,input[type='color']::-webkit-input-placeholder,textarea::-webkit-input-placeholder,select::-webkit-input-placeholder{color:#ccc}input[type='email']:-ms-input-placeholder,input[type='number']:-ms-input-placeholder,input[type='password']:-ms-input-placeholder,input[type='search']:-ms-input-placeholder,input[type='tel']:-ms-input-placeholder,input[type='text']:-ms-input-placeholder,input[type='url']:-ms-input-placeholder,input[type='date']:-ms-input-placeholder,input[type='time']:-ms-input-placeholder,input[type='color']:-ms-input-placeholder,textarea:-ms-input-placeholder,select:-ms-input-placeholder{color:#ccc}input[type='email']::-ms-input-placeholder,input[type='number']::-ms-input-placeholder,input[type='password']::-ms-input-placeholder,input[type='search']::-ms-input-placeholder,input[type='tel']::-ms-input-placeholder,input[type='text']::-ms-input-placeholder,input[type='url']::-ms-input-placeholder,input[type='date']::-ms-input-placeholder,input[type='time']::-ms-input-placeholder,input[type='color']::-ms-input-placeholder,textarea::-ms-input-placeholder,select::-ms-input-placeholder{color:#ccc}input[type='email']::placeholder,input[type='number']::placeholder,input[type='password']::placeholder,input[type='search']::placeholder,input[type='tel']::placeholder,input[type='text']::placeholder,input[type='url']::placeholder,input[type='date']::placeholder,input[type='time']::placeholder,input[type='color']::placeholder,textarea::placeholder,select::placeholder{color:#ccc}::-moz-selection{color:white;background-color:#00a1cc}::selection{color:white;background-color:#00a1cc}.navbar{background-color:#f7f7f7;-webkit-box-sizing:border-box;box-sizing:border-box;padding:10px 0;margin-bottom:50px;border-bottom:#ccc 0.5px solid}.navbar .navbar__wrapper{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;position:relative}.navbar .navbar__logo{-ms-flex-preferred-size:100px;flex-basis:100px}.navbar .navbar__logo-link{display:inline-block}.navbar .navbar__link-list{margin:0;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-pack:distribute;justify-content:space-around}.navbar .navbar__link{margin:9px;color:#606c76}.navbar .navbar__link:active,.navbar .navbar__link:hover,.navbar .navbar__link:hover:visited{color:#00a1cc}.navbar .navbar__link:visited{color:#606c76}.navbar .navbar__search-box{margin:0 12% 0 15px;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-flex:1;-ms-flex:1;flex:1}.navbar .navbar__search-box>input[type="search"]{border-top-right-radius:0;border-bottom-right-radius:0;margin:0;height:32px;background-color:white !important;width:100%}.navbar .navbar__search-box .navbar__search-dropdown{margin:0;margin-left:-1px;padding:0;padding-left:10px;color:#606c76;-webkit-appearance:auto;-moz-appearance:auto;appearance:auto;background-color:white;height:32px;width:80px;border-top-left-radius:0;border-bottom-left-radius:0}.navbar .navbar__dropdown-btn{display:none;padding:0;margin:0;border:none;background-color:transparent;color:#00a1cc}.navbar .navbar__dropdown-btn:focus,.navbar .navbar__dropdown-btn:hover{background-color:transparent;color:#606c76}@media (max-width: 575.98px){.navbar{padding:2px 0}.navbar .navbar__wrapper{display:block}.navbar .navbar__logo-img{width:72px;margin-right:10px;position:relative;top:7px}.navbar .navbar__link-list{margin-top:7px;max-height:0;-webkit-transition:max-height 0.6s ease-out;transition:max-height 0.6s ease-out;overflow:hidden}.navbar .navbar__dropdown-btn{display:block;position:absolute;right:5px;top:3px;-webkit-transform:scale(0.7);transform:scale(0.7)}.navbar .navbar__dropdown-btn:hover+.navbar__link-list{max-height:500px;-webkit-transition:max-height 0.6s ease-in;transition:max-height 0.6s ease-in}.navbar .navbar__search-box{margin:0;width:46vw}.navbar .navbar__search-box>input[type="search"]{height:26px;padding:4px 6px;width:32vw}.navbar .navbar__search-box .navbar__search-dropdown{cursor:pointer;height:26px;width:80px;padding-left:5px}}@media (max-width: 991.98px){.navbar{margin-bottom:20px}}.grid{margin:0 auto;position:relative;max-width:110rem;padding:0 2.0rem;width:100%}.grid .grid__main{width:70%;float:left;position:relative}.grid .grid__aside{width:26%;float:right;position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:distribute;justify-content:space-around}.grid::after{content:' ';clear:both;display:table}@media (max-width: 575.98px){.grid .grid__aside{-webkit-box-orient:vertical !important;-webkit-box-direction:normal !important;-ms-flex-direction:column !important;flex-direction:column !important}}@media (max-width: 991.98px){.grid .grid__main{width:100%;float:none}.grid .grid__aside{width:100%;float:none;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.grid .grid__aside--tablet-column{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.grid--reverse-order{-webkit-transform:scaleY(-1);transform:scaleY(-1)}.grid .grid__main--reverse-order{-webkit-transform:scaleY(-1);transform:scaleY(-1)}.grid .grid__aside--reverse-order{-webkit-transform:scaleY(-1);transform:scaleY(-1)}}.pagination{text-align:center;width:100%}.pagination .pagination__page-link{font-weight:normal;margin:0 5px}.pagination .pagination__page-link--current{font-weight:bold;font-size:1.2em;color:#606c76}.pagination .pagination__nav-link{font-size:1.4em;margin:0 2px}.pagination .pagination__nav-link--right-margin{margin-right:18px}.pagination .pagination__nav-link--left-margin{margin-left:18px}.pagination .pagination__nav-link--hidden{display:none}@media (max-width: 575.98px){.pagination .pagination__page-link{margin:0 3px}.pagination .pagination__nav-link{font-size:1.4em;margin:0 2px}.pagination .pagination__nav-link--right-margin{margin-right:10px}.pagination .pagination__nav-link--left-margin{margin-left:10px}}#page-wrapper{position:relative;min-height:100vh;z-index:0}#content-wrapper{padding-bottom:160px}.footer{padding-top:0.4em !important;text-align:center;margin-bottom:4px !important;position:absolute !important;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%);bottom:0;width:100%}.footer__border{padding-top:4px;border-top:#f7f7f7 solid 2px}.footer__link{margin:0 12px;white-space:nowrap}@media (max-width: 575.98px){#content-wrapper{padding-bottom:120px}}.icon-lock svg{fill:#ccc;height:12px;position:relative;top:1px;margin-left:3px}.icon-edit svg{fill:#ccc;height:12px;position:relative;top:2px}.icon-save svg{fill:#ccc;height:12px;position:relative;top:2px}.icon-cross svg{fill:#ccc;height:10px;position:relative}.icon-arrow svg{fill:#606c76;height:15px;position:relative;top:3px}.spinner{display:inline-block;position:relative;left:50%;-webkit-transform:translateX(-50%) scale(0.4);transform:translateX(-50%) scale(0.4);width:80px;height:80px}.spinner div{-webkit-transform-origin:40px 40px;transform-origin:40px 40px;-webkit-animation:spinner 1.2s linear infinite;animation:spinner 1.2s linear infinite}.spinner div::after{content:" ";display:block;position:absolute;top:3px;left:37px;width:6px;height:18px;border-radius:20%;background:#606c76}.spinner div:nth-child(1){-webkit-transform:rotate(0deg);transform:rotate(0deg);-webkit-animation-delay:-1.1s;animation-delay:-1.1s}.spinner div:nth-child(2){-webkit-transform:rotate(30deg);transform:rotate(30deg);-webkit-animation-delay:-1s;animation-delay:-1s}.spinner div:nth-child(3){-webkit-transform:rotate(60deg);transform:rotate(60deg);-webkit-animation-delay:-.9s;animation-delay:-.9s}.spinner div:nth-child(4){-webkit-transform:rotate(90deg);transform:rotate(90deg);-webkit-animation-delay:-.8s;animation-delay:-.8s}.spinner div:nth-child(5){-webkit-transform:rotate(120deg);transform:rotate(120deg);-webkit-animation-delay:-.7s;animation-delay:-.7s}.spinner div:nth-child(6){-webkit-transform:rotate(150deg);transform:rotate(150deg);-webkit-animation-delay:-.6s;animation-delay:-.6s}.spinner div:nth-child(7){-webkit-transform:rotate(180deg);transform:rotate(180deg);-webkit-animation-delay:-.5s;animation-delay:-.5s}.spinner div:nth-child(8){-webkit-transform:rotate(210deg);transform:rotate(210deg);-webkit-animation-delay:-.4s;animation-delay:-.4s}.spinner div:nth-child(9){-webkit-transform:rotate(240deg);transform:rotate(240deg);-webkit-animation-delay:-.3s;animation-delay:-.3s}.spinner div:nth-child(10){-webkit-transform:rotate(270deg);transform:rotate(270deg);-webkit-animation-delay:-.2s;animation-delay:-.2s}.spinner div:nth-child(11){-webkit-transform:rotate(300deg);transform:rotate(300deg);-webkit-animation-delay:-.1s;animation-delay:-.1s}.spinner div:nth-child(12){-webkit-transform:rotate(330deg);transform:rotate(330deg);-webkit-animation-delay:0s;animation-delay:0s}@-webkit-keyframes spinner{0%{opacity:1}100%{opacity:0}}@keyframes spinner{0%{opacity:1}100%{opacity:0}}.bg-mask{background-color:black;z-index:1;-webkit-filter:opacity(20%);filter:opacity(20%);position:fixed;width:100%;height:100%;left:0;top:0;display:none}.mark-modal{z-index:2;display:none;position:fixed;width:500px;top:50%;left:50%;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);background-color:#f7f7f7;padding:20px 20px 10px 20px;color:#606c76}.mark-modal .mark-modal__head{margin-bottom:20px}.mark-modal .mark-modal__head::after{content:' ';clear:both;display:table}.mark-modal .mark-modal__title{font-weight:bold;font-size:1.2em;float:left}.mark-modal .mark-modal__close-button{float:right;cursor:pointer}.mark-modal .mark-modal__confirm-button{float:right}.mark-modal input[type="radio"]{margin-right:0}.mark-modal .mark-modal__rating-star{display:inline;float:left;position:relative;left:-3px}.mark-modal .mark-modal__status-radio{float:right}.mark-modal .mark-modal__status-radio ul{margin-bottom:0}.mark-modal .mark-modal__status-radio li,.mark-modal .mark-modal__status-radio label{display:inline}.mark-modal .mark-modal__status-radio input[type="radio"]{position:relative;top:1px}.mark-modal .mark-modal__clear{content:' ';clear:both;display:table}.mark-modal .mark-modal__content-input,.mark-modal form textarea{height:200px;width:100%;margin-top:5px;margin-bottom:5px;resize:vertical}.mark-modal .mark-modal__tag{margin-bottom:20px}.mark-modal .mark-modal__option{margin-bottom:24px}.mark-modal .mark-modal__option::after{content:' ';clear:both;display:table}.mark-modal .mark-modal__visibility-radio{float:left}.mark-modal .mark-modal__visibility-radio ul,.mark-modal .mark-modal__visibility-radio li,.mark-modal .mark-modal__visibility-radio label{display:inline}.mark-modal .mark-modal__visibility-radio label{font-size:normal}.mark-modal .mark-modal__visibility-radio input[type="radio"]{position:relative;top:2px}.mark-modal .mark-modal__share-checkbox{float:right}.mark-modal .mark-modal__share-checkbox input[type="checkbox"]{position:relative;top:2px}.confirm-modal{z-index:2;display:none;position:fixed;width:500px;top:50%;left:50%;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);background-color:#f7f7f7;padding:20px 20px 10px 20px;color:#606c76}.confirm-modal .confirm-modal__head{margin-bottom:20px}.confirm-modal .confirm-modal__head::after{content:' ';clear:both;display:table}.confirm-modal .confirm-modal__title{font-weight:bold;font-size:1.2em;float:left}.confirm-modal .confirm-modal__close-button{float:right;cursor:pointer}.confirm-modal .confirm-modal__confirm-button{float:right}.announcement-modal{z-index:2;display:none;position:fixed;width:500px;top:50%;left:50%;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);background-color:#f7f7f7;padding:20px 20px 10px 20px;color:#606c76}.announcement-modal .announcement-modal__head{margin-bottom:20px}.announcement-modal .announcement-modal__head::after{content:' ';clear:both;display:table}.announcement-modal .announcement-modal__title{font-weight:bold;font-size:1.2em;float:left}.announcement-modal .announcement-modal__close-button{float:right;cursor:pointer}.announcement-modal .announcement-modal__confirm-button{float:right}.announcement-modal .announcement-modal__body{overflow-y:auto;max-height:64vh}.announcement-modal .announcement-modal__body .announcement__title{display:inline-block}.announcement-modal .announcement-modal__body .announcement__datetime{color:#ccc;margin-left:10px}.announcement-modal .announcement-modal__body .announcement__content{word-break:break-all}@media (max-width: 575.98px){.mark-modal,.confirm-modal,.announcement-modal{width:100%}}.source-label{display:inline;background:transparent;border-radius:.3rem;border-style:solid;border-width:.1rem;line-height:1.2rem;font-size:1.1rem;margin:3px;padding:1px 3px;padding-top:2px;font-weight:lighter;letter-spacing:0.1rem;word-break:keep-all;opacity:1;position:relative;top:-1px}.source-label.source-label__in-site{border-color:#00a1cc;color:#00a1cc}.source-label.source-label__douban{border:none;color:#fff;background-color:#319840}.source-label.source-label__spotify{background-color:#1ed760;color:#000;border:none;font-weight:bold}.source-label.source-label__imdb{background-color:#F5C518;color:#121212;border:none;font-weight:bold}.main-section-wrapper{padding:32px 48px 32px 36px;background-color:#f7f7f7;overflow:auto}.main-section-wrapper input,.main-section-wrapper select{width:100%}.entity-list .entity-list__title{margin-bottom:20px}.entity-list .entity-list__entity{display:-webkit-box;display:-ms-flexbox;display:flex;margin-bottom:36px}.entity-list .entity-list__entity::after{content:' ';clear:both;display:table}.entity-list .entity-list__entity-img{-o-object-fit:contain;object-fit:contain;min-width:130px;max-width:130px}.entity-list .entity-list__entity-text{margin-left:20px;overflow:hidden;width:100%}.entity-list .entity-list__entity-text .tag-collection{margin-left:-3px}.entity-list .entity-list__entity-link{font-size:1.2em}.entity-list .entity-list__entity-title{display:block}.entity-list .entity-list__entity-category{color:#bbb;margin-left:5px;position:relative;top:-1px}.entity-list .entity-list__entity-info{max-width:73%;white-space:nowrap;overflow:hidden;display:inline-block;text-overflow:ellipsis;position:relative;top:0.52em}.entity-list .entity-list__entity-info--full-length{max-width:100%}.entity-list .entity-list__entity-brief{margin-top:8px;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:4;overflow:hidden;margin-bottom:0}.entity-list .entity-list__rating{display:inline-block;margin:0}.entity-list .entity-list__rating--empty{margin-right:5px}.entity-list .entity-list__rating-score{margin-right:5px;position:relative;top:1px}.entity-list .entity-list__rating-star{display:inline;position:relative;top:0.3em;left:-0.3em}.entity-detail .entity-detail__img{height:210px;float:left;-o-object-fit:contain;object-fit:contain;max-width:150px;-o-object-position:top;object-position:top}.entity-detail .entity-detail__img-origin{cursor:-webkit-zoom-in;cursor:zoom-in}.entity-detail .entity-detail__info{float:left;margin-left:20px;overflow:hidden;text-overflow:ellipsis;width:70%}.entity-detail .entity-detail__title{font-weight:bold}.entity-detail .entity-detail__title--secondary{color:#bbb}.entity-detail .entity-detail__fields{display:inline-block;vertical-align:top;width:46%;margin-left:2%}.entity-detail .entity-detail__fields div,.entity-detail .entity-detail__fields span{margin:1px 0}.entity-detail .entity-detail__fields+.tag-collection{margin-top:5px;margin-left:6px}.entity-detail .entity-detail__rating{position:relative;top:-5px}.entity-detail .entity-detail__rating-star{position:relative;left:-4px;top:3px}.entity-detail .entity-detail__rating-score{font-weight:bold}.entity-detail::after{content:' ';clear:both;display:table}.entity-desc{margin-bottom:28px}.entity-desc .entity-desc__title{display:inline-block;margin-bottom:8px}.entity-desc .entity-desc__content{overflow:hidden}.entity-desc .entity-desc__content--folded{max-height:202px}.entity-desc .entity-desc__unfold-button{display:-webkit-box;display:-ms-flexbox;display:flex;color:#00a1cc;background-color:transparent;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;text-align:center}.entity-desc .entity-desc__unfold-button--hidden{display:none}.entity-marks{margin-bottom:28px}.entity-marks .entity-marks__title{margin-bottom:8px;display:inline-block}.entity-marks .entity-marks__title>a{margin-right:5px}.entity-marks .entity-marks__title--stand-alone{margin-bottom:20px}.entity-marks .entity-marks__more-link{margin-left:5px}.entity-marks .entity-marks__mark{margin:0;padding:3px 0;border-bottom:1px dashed #e5e5e5}.entity-marks .entity-marks__mark:last-child{border:none}.entity-marks .entity-marks__mark--wider{padding:6px 0}.entity-marks .entity-marks__mark-content{margin-bottom:0}.entity-marks .entity-marks__mark-time{color:#ccc;margin-left:2px}.entity-marks .entity-marks__rating-star{position:relative;top:4px}.entity-reviews:first-child{margin-bottom:28px}.entity-reviews .entity-reviews__title{display:inline-block;margin-bottom:8px}.entity-reviews .entity-reviews__title>a{margin-right:5px}.entity-reviews .entity-reviews__title--stand-alone{margin-bottom:20px}.entity-reviews .entity-reviews__more-link{margin-left:5px}.entity-reviews .entity-reviews__review{margin:0;padding:3px 0;border-bottom:1px dashed #e5e5e5}.entity-reviews .entity-reviews__review:last-child{border:none}.entity-reviews .entity-reviews__review--wider{padding:6px 0}.entity-reviews .entity-reviews__review-time{color:#ccc;margin-left:2px}.dividing-line{height:0;width:100%;margin:40px 0 24px 0;border-top:solid 1px #ccc}.dividing-line.dividing-line--dashed{margin:0;margin-top:10px;margin-bottom:2px;border-top:1px dashed #e5e5e5}.entity-sort{position:relative;margin-bottom:30px}.entity-sort .entity-sort__label{font-size:large;display:inline-block;margin-bottom:20px}.entity-sort .entity-sort__more-link{margin-left:5px}.entity-sort .entity-sort__entity-list{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;-ms-flex-wrap:wrap;flex-wrap:wrap}.entity-sort .entity-sort__entity{padding:0 10px;-ms-flex-preferred-size:20%;flex-basis:20%;text-align:center;display:inline-block;color:#606c76}.entity-sort .entity-sort__entity:hover{color:#00a1cc}.entity-sort .entity-sort__entity>a{color:inherit}.entity-sort .entity-sort__entity-img{height:110px}.entity-sort .entity-sort__entity-name{text-overflow:ellipsis;overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.entity-sort--placeholder{border:dashed #bbb 4px}.entity-sort--hover{padding:10px;border:dashed #00a1cc 2px !important;border-radius:3px}.entity-sort--sortable{padding:10px;margin:10px 0;border:dashed #bbb 2px;cursor:all-scroll}.entity-sort--hidden{opacity:0.4}.entity-sort-control{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end}.entity-sort-control__button{margin-top:5px;margin-left:12px;padding:0 2px;cursor:pointer;color:#bbb}.entity-sort-control__button:hover{color:#00a1cc}.entity-sort-control__button:hover>.icon-save svg,.entity-sort-control__button:hover>.icon-edit svg{fill:#00a1cc}.entity-sort-control__button--float-right{position:absolute;top:4px;right:10px}.related-user-list .related-user-list__title{margin-bottom:20px}.related-user-list .related-user-list__user{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;margin-bottom:20px}.related-user-list .related-user-list__user-info{margin-left:15px;overflow:auto}.related-user-list .related-user-list__user-avatar{max-height:72px;min-width:72px}.review-head .review-head__title{display:inline-block;font-weight:bold}.review-head .review-head__body{margin-bottom:10px}.review-head .review-head__body::after{content:' ';clear:both;display:table}.review-head .review-head__info{float:left}.review-head .review-head__owner-link{color:#ccc}.review-head .review-head__owner-link:hover{color:#00a1cc}.review-head .review-head__time{color:#ccc}.review-head .review-head__rating-star{position:relative;top:3px;left:-1px}.review-head .review-head__actions{float:right}.review-head .review-head__action-link:not(:first-child){margin-left:5px}.tag-collection{margin-left:-9px}.tag-collection .tag-collection__tag{position:relative;display:block;float:left;color:white;background:#ccc;padding:5px;border-radius:.3rem;line-height:1.2em;font-size:80%;margin:3px}.tag-collection .tag-collection__tag a{color:white}.tag-collection .tag-collection__tag a:hover{color:#00a1cc}.track-carousel{position:relative;margin-top:5px}.track-carousel__content{overflow:auto;scroll-behavior:smooth;scrollbar-width:none;display:-webkit-box;display:-ms-flexbox;display:flex;margin:auto;-webkit-box-sizing:border-box;box-sizing:border-box;padding-bottom:10px}.track-carousel__content::-webkit-scrollbar{height:3px;width:1px;background-color:#e5e5e5}.track-carousel__content::-webkit-scrollbar-thumb{background-color:#bbb}.track-carousel__track{text-align:center;overflow:hidden;text-overflow:ellipsis;min-width:18%;max-width:18%;margin-right:2.5%}.track-carousel__track img{-o-object-fit:contain;object-fit:contain}.track-carousel__track-title{white-space:nowrap}.track-carousel__button{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-ms-flex-line-pack:center;align-content:center;background:white;border:none;padding:8px;border-radius:50%;outline:0;cursor:pointer;position:absolute;top:50%}.track-carousel__button--prev{left:0;-webkit-transform:translate(50%, -50%);transform:translate(50%, -50%)}.track-carousel__button--next{right:0;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%)}@media (max-width: 575.98px){.entity-list .entity-list__entity{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin-bottom:30px}.entity-list .entity-list__entity-text{margin-left:0}.entity-list .entity-list__entity-img-wrapper{margin-bottom:8px}.entity-list .entity-list__entity-info{max-width:unset}.entity-list .entity-list__rating--empty+.entity-list__entity-info{max-width:70%}.entity-list .entity-list__entity-brief{-webkit-line-clamp:5}.entity-detail{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.entity-detail .entity-detail__title{margin-bottom:5px}.entity-detail .entity-detail__info{margin-left:0;float:none;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;width:100%}.entity-detail .entity-detail__img{margin-bottom:24px;float:none;height:unset;max-width:170px}.entity-detail .entity-detail__fields{width:unset;margin-left:unset}.entity-detail .entity-detail__fields+.tag-collection{margin-left:-3px}.dividing-line{margin-top:24px}.entity-sort .entity-sort__entity{-ms-flex-preferred-size:50%;flex-basis:50%}.entity-sort .entity-sort__entity-img{height:130px}.review-head .review-head__info{float:unset}.review-head .review-head__actions{float:unset}.track-carousel__content{padding-bottom:10px}.track-carousel__track{min-width:31%;max-width:31%;margin-right:4.5%}}@media (max-width: 991.98px){.main-section-wrapper{padding:32px 28px 28px 28px}.entity-detail{display:-webkit-box;display:-ms-flexbox;display:flex}}.aside-section-wrapper{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-ms-flex:1;flex:1;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;width:100%;padding:28px 25px 12px 25px;background-color:#f7f7f7;margin-bottom:30px;overflow:auto}.aside-section-wrapper--transparent{background-color:unset}.aside-section-wrapper--collapse{padding:unset}.add-entity-entries .add-entity-entries__entry{margin-bottom:10px}.add-entity-entries .add-entity-entries__label{font-size:1.2em;margin-bottom:8px}.add-entity-entries .add-entity-entries__button{line-height:unset;height:unset;padding:4px 15px;margin:5px}.action-panel{margin-bottom:20px}.action-panel .action-panel__label{font-weight:bold;margin-bottom:12px}.action-panel .action-panel__button-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.action-panel .action-panel__button-group--center{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.action-panel .action-panel__button{line-height:unset;height:unset;padding:4px 15px;margin:0 5px}.mark-panel{margin-bottom:20px}.mark-panel .mark-panel__status{font-weight:bold}.mark-panel .mark-panel__rating-star{position:relative;top:2px}.mark-panel .mark-panel__actions{float:right}.mark-panel .mark-panel__actions form{display:inline}.mark-panel .mark-panel__time{color:#ccc;margin-bottom:10px}.mark-panel .mark-panel__clear{content:' ';clear:both;display:table}.review-panel .review-panel__label{font-weight:bold}.review-panel .review-panel__actions{float:right}.review-panel .review-panel__time{color:#ccc;margin-bottom:10px}.review-panel .review-panel__review-title{display:block;margin-bottom:15px;font-weight:bold}.review-panel .review-panel__clear{content:' ';clear:both;display:table}.user-profile .user-profile__header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;margin-bottom:15px}.user-profile .user-profile__avatar{width:72px}.user-profile .user-profile__username{font-size:large;margin-left:10px;margin-bottom:0}.user-profile .user-profile__report-link{color:#ccc}.user-relation .user-relation__label{display:inline-block;font-size:large;margin-bottom:10px}.user-relation .user-relation__more-link{margin-left:5px}.user-relation .user-relation__related-user-list{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.user-relation .user-relation__related-user{-ms-flex-preferred-size:25%;flex-basis:25%;padding:0px 3px;text-align:center;display:inline-block;overflow:hidden}.user-relation .user-relation__related-user>a:hover{color:#606c76}.user-relation .user-relation__related-user-avatar{width:48px}.user-relation .user-relation__related-user-name{color:inherit;overflow:hidden;text-overflow:ellipsis;-webkit-box-orient:vertical;-webkit-line-clamp:2}.report-panel .report-panel__label{display:inline-block;margin-bottom:10px}.report-panel .report-panel__report{margin:2px 0}.report-panel .report-panel__user-link{margin:0 2px}.report-panel .report-panel__all-link{margin-left:5px}.relation-dropdown .relation-dropdown__button{display:none}.entity-card{display:-webkit-box;display:-ms-flexbox;display:flex;margin-bottom:10px;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.entity-card--horizontal{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.entity-card .entity-card__img{height:150px}.entity-card .entity-card__rating-star{position:relative;top:4px;left:-3px}.entity-card .entity-card__rating-score{position:relative;top:1px;margin-left:2px}.entity-card .entity-card__title{margin-bottom:10px;margin-top:5px}.entity-card .entity-card__info-wrapper--horizontal{margin-left:20px}.entity-card .entity-card__img-wrapper{-ms-flex-preferred-size:100px;flex-basis:100px}@media (max-width: 575.98px){.add-entity-entries{display:block !important}.add-entity-entries .add-entity-entries__button{width:100%;margin:5px 0 5px 0}.aside-section-wrapper:first-child{margin-right:0 !important;margin-bottom:0 !important}.aside-section-wrapper--singular:first-child{margin-bottom:20px !important}.action-panel{-webkit-box-orient:vertical !important;-webkit-box-direction:normal !important;-ms-flex-direction:column !important;flex-direction:column !important}.entity-card--horizontal{-webkit-box-orient:vertical !important;-webkit-box-direction:normal !important;-ms-flex-direction:column !important;flex-direction:column !important}.entity-card .entity-card__info-wrapper{margin-left:10px !important}.entity-card .entity-card__info-wrapper--horizontal{margin-left:0 !important}}@media (max-width: 991.98px){.add-entity-entries{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-pack:distribute;justify-content:space-around}.aside-section-wrapper{padding:24px 25px 10px 25px;margin-top:20px}.aside-section-wrapper:not(:last-child){margin-right:20px}.aside-section-wrapper--collapse{padding:24px 25px 10px 25px !important;margin-top:0;margin-bottom:0}.aside-section-wrapper--collapse:first-child{margin-right:0}.aside-section-wrapper--no-margin{margin:0}.action-panel{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.action-panel .action-panel__button-group{-webkit-box-pack:space-evenly;-ms-flex-pack:space-evenly;justify-content:space-evenly}.relation-dropdown{margin-bottom:20px}.relation-dropdown .relation-dropdown__button{padding-bottom:10px;background-color:#f7f7f7;width:100%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;cursor:pointer}.relation-dropdown .relation-dropdown__button:focus{background-color:red}.relation-dropdown .relation-dropdown__button>.icon-arrow{-webkit-transition:-webkit-transform 0.3s;transition:-webkit-transform 0.3s;transition:transform 0.3s;transition:transform 0.3s, -webkit-transform 0.3s}.relation-dropdown .relation-dropdown__button:hover>.icon-arrow>svg{fill:#00a1cc}.relation-dropdown .relation-dropdown__button>.icon-arrow--expand{-webkit-transform:rotate(-180deg);transform:rotate(-180deg)}.relation-dropdown .relation-dropdown__button+.relation-dropdown__body--expand{max-height:500px;-webkit-transition:max-height 0.6s ease-in;transition:max-height 0.6s ease-in}.relation-dropdown .relation-dropdown__body{background-color:#f7f7f7;max-height:0;-webkit-transition:max-height 0.6s ease-out;transition:max-height 0.6s ease-out;overflow:hidden}.entity-card{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.entity-card .entity-card__info-wrapper{margin-left:30px}}.single-section-wrapper{padding:32px 36px;background-color:#f7f7f7;overflow:auto}.single-section-wrapper .single-section-wrapper__link--secondary{display:inline-block;color:#ccc;margin-bottom:20px}.single-section-wrapper .single-section-wrapper__link--secondary:hover{color:#00a1cc}.entity-form,.review-form{overflow:auto}.entity-form>input[type='email'],.entity-form>input[type='number'],.entity-form>input[type='password'],.entity-form>input[type='search'],.entity-form>input[type='tel'],.entity-form>input[type='text'],.entity-form>input[type='url'],.entity-form textarea,.review-form>input[type='email'],.review-form>input[type='number'],.review-form>input[type='password'],.review-form>input[type='search'],.review-form>input[type='tel'],.review-form>input[type='text'],.review-form>input[type='url'],.review-form textarea{width:100%}.entity-form img,.review-form img{display:block}.review-form .review-form__preview-button{color:#00a1cc;font-weight:bold;cursor:pointer}.review-form .review-form__fyi{color:#ccc}.review-form .review-form__main-content,.review-form textarea{margin-bottom:5px;resize:vertical;height:400px}.review-form .review-form__option{margin-top:24px;margin-bottom:10px}.review-form .review-form__option::after{content:' ';clear:both;display:table}.review-form .review-form__visibility-radio{float:left}.review-form .review-form__visibility-radio ul,.review-form .review-form__visibility-radio li,.review-form .review-form__visibility-radio label{display:inline}.review-form .review-form__visibility-radio label{font-size:normal}.review-form .review-form__visibility-radio input[type="radio"]{position:relative;top:2px}.review-form .review-form__share-checkbox{float:right}.review-form .review-form__share-checkbox input[type="checkbox"]{position:relative;top:2px}.report-form input,.report-form select{width:100%}@media (max-width: 575.98px){.review-form .review-form__visibility-radio{float:unset}.review-form .review-form__share-checkbox{float:unset;position:relative;left:-3px}}.markdownx-preview{min-height:100px}.markdownx-preview ul li{list-style:circle inside}.rating-star .jq-star{cursor:unset !important}.ms-parent>.ms-choice{margin-bottom:1.5rem;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:transparent;border:0.1rem solid #ccc;border-radius:.4rem;-webkit-box-shadow:none;box-shadow:none;-webkit-box-sizing:inherit;box-sizing:inherit;padding:.6rem 1.0rem;width:100%;height:30.126px}.ms-parent>.ms-choice:focus{border-color:#00a1cc}.ms-parent>.ms-choice>.icon-caret{top:15.5px}.ms-parent>.ms-choice>span{color:black;font-weight:initial;font-size:13.3333px;top:2.5px;left:2px}.ms-parent>.ms-choice>span:hover,.ms-parent>.ms-choice>span:focus{color:black}.ms-parent>.ms-drop>ul>li>label>span{margin-left:10px}.ms-parent>.ms-drop>ul>li>label>input{width:unset}
+@import url(https://cdn.jsdelivr.net/npm/skeleton-css@2.0.4/css/normalize.css);.button,button,input[type='button'],input[type='reset'],input[type='submit']{background-color:#00a1cc;border:0.1rem solid #00a1cc;border-radius:.4rem;color:#fff;cursor:pointer;display:inline-block;font-size:1.1rem;font-weight:700;height:3.4rem;letter-spacing:.1rem;line-height:3.4rem;padding:0 2.8rem;text-align:center;text-decoration:none;text-transform:uppercase;white-space:nowrap}.button:focus,.button:hover,button:focus,button:hover,input[type='button']:focus,input[type='button']:hover,input[type='reset']:focus,input[type='reset']:hover,input[type='submit']:focus,input[type='submit']:hover{background-color:#606c76;border-color:#606c76;color:#fff;outline:0}.button[disabled],button[disabled],input[type='button'][disabled],input[type='reset'][disabled],input[type='submit'][disabled]{cursor:default;opacity:.5}.button[disabled]:focus,.button[disabled]:hover,button[disabled]:focus,button[disabled]:hover,input[type='button'][disabled]:focus,input[type='button'][disabled]:hover,input[type='reset'][disabled]:focus,input[type='reset'][disabled]:hover,input[type='submit'][disabled]:focus,input[type='submit'][disabled]:hover{background-color:#00a1cc;border-color:#00a1cc}.button.button-outline,button.button-outline,input[type='button'].button-outline,input[type='reset'].button-outline,input[type='submit'].button-outline{background-color:transparent;color:#00a1cc}.button.button-outline:focus,.button.button-outline:hover,button.button-outline:focus,button.button-outline:hover,input[type='button'].button-outline:focus,input[type='button'].button-outline:hover,input[type='reset'].button-outline:focus,input[type='reset'].button-outline:hover,input[type='submit'].button-outline:focus,input[type='submit'].button-outline:hover{background-color:transparent;border-color:#606c76;color:#606c76}.button.button-outline[disabled]:focus,.button.button-outline[disabled]:hover,button.button-outline[disabled]:focus,button.button-outline[disabled]:hover,input[type='button'].button-outline[disabled]:focus,input[type='button'].button-outline[disabled]:hover,input[type='reset'].button-outline[disabled]:focus,input[type='reset'].button-outline[disabled]:hover,input[type='submit'].button-outline[disabled]:focus,input[type='submit'].button-outline[disabled]:hover{border-color:inherit;color:#00a1cc}.button.button-clear,button.button-clear,input[type='button'].button-clear,input[type='reset'].button-clear,input[type='submit'].button-clear{background-color:transparent;border-color:transparent;color:#00a1cc}.button.button-clear:focus,.button.button-clear:hover,button.button-clear:focus,button.button-clear:hover,input[type='button'].button-clear:focus,input[type='button'].button-clear:hover,input[type='reset'].button-clear:focus,input[type='reset'].button-clear:hover,input[type='submit'].button-clear:focus,input[type='submit'].button-clear:hover{background-color:transparent;border-color:transparent;color:#606c76}.button.button-clear[disabled]:focus,.button.button-clear[disabled]:hover,button.button-clear[disabled]:focus,button.button-clear[disabled]:hover,input[type='button'].button-clear[disabled]:focus,input[type='button'].button-clear[disabled]:hover,input[type='reset'].button-clear[disabled]:focus,input[type='reset'].button-clear[disabled]:hover,input[type='submit'].button-clear[disabled]:focus,input[type='submit'].button-clear[disabled]:hover{color:#00a1cc}select{background:url('data:image/svg+xml;utf8,
') center right no-repeat;padding-right:3.0rem}select:focus{background-image:url('data:image/svg+xml;utf8,
')}textarea{min-height:6.5rem;width:100%}select{width:100%}label,legend{display:block;margin-bottom:.5rem}fieldset{border-width:0;padding:0}input[type='checkbox'],input[type='radio']{display:inline}.label-inline{display:inline-block;font-weight:normal;margin-left:.5rem}dl,ol,ul{list-style:none;margin-top:0;padding-left:0}ol{list-style:decimal inside}ul{list-style:circle inside}.button,button,dd,dt,li{margin-bottom:1.0rem}fieldset,input,select,textarea{margin-bottom:1.5rem}blockquote,dl,figure,form,ol,p,pre,table,ul{margin-bottom:1rem}b,strong{font-weight:bold}p{margin-top:0}h1,h2,h3,h4,h5,h6{font-weight:300;letter-spacing:-.1rem;margin-bottom:2.0rem;margin-top:0}h1{font-size:4.6rem;line-height:1.2}h2{font-size:3.6rem;line-height:1.25}h3{font-size:2.8rem;line-height:1.3}h4{font-size:2.2rem;letter-spacing:-.08rem;line-height:1.35}h5{font-size:1.8rem;letter-spacing:-.05rem;line-height:1.5}h6{font-size:1.6rem;letter-spacing:0;line-height:1.4}img{max-width:100%;-o-object-fit:contain;object-fit:contain}img.emoji{height:14px;-webkit-box-sizing:border-box;box-sizing:border-box;-o-object-fit:contain;object-fit:contain;position:relative;top:3px}img.emoji--large{height:20px;position:relative;top:2px}.clearfix:after{clear:both;content:' ';display:table}.float-left{float:left}.float-right{float:right}.highlight{font-weight:bold}:root{font-size:10px}*,*:after,*:before{-webkit-box-sizing:inherit;box-sizing:inherit}html{-webkit-box-sizing:border-box;box-sizing:border-box;height:100%}body{color:#606c76;font-family:'Roboto', 'Helvetica Neue', 'Helvetica', 'Arial', 'Microsoft YaHei Light', sans-serif;font-size:1.3rem;font-weight:300;letter-spacing:.05rem;line-height:1.6;margin:0;height:100%}textarea{font-family:'Roboto', 'Helvetica Neue', 'Helvetica', 'Arial', 'Microsoft YaHei Light', sans-serif}a{color:#00a1cc;text-decoration:none}a:active,a:hover,a:hover:visited{color:#606c76}li{list-style:none}input[type=text]::-ms-clear,input[type=text]::-ms-reveal{display:none;width:0;height:0}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-results-button,input[type="search"]::-webkit-search-results-decoration{display:none}input[type='email'],input[type='number'],input[type='password'],input[type='search'],input[type='tel'],input[type='text'],input[type='url'],input[type='date'],input[type='time'],input[type='color'],textarea,select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:transparent;border:0.1rem solid #ccc;border-radius:.4rem;-webkit-box-shadow:none;box-shadow:none;-webkit-box-sizing:inherit;box-sizing:inherit;padding:.6rem 1.0rem}input[type='email']:focus,input[type='number']:focus,input[type='password']:focus,input[type='search']:focus,input[type='tel']:focus,input[type='text']:focus,input[type='url']:focus,input[type='date']:focus,input[type='time']:focus,input[type='color']:focus,textarea:focus,select:focus{border-color:#00a1cc;outline:0}input[type='email']::-webkit-input-placeholder,input[type='number']::-webkit-input-placeholder,input[type='password']::-webkit-input-placeholder,input[type='search']::-webkit-input-placeholder,input[type='tel']::-webkit-input-placeholder,input[type='text']::-webkit-input-placeholder,input[type='url']::-webkit-input-placeholder,input[type='date']::-webkit-input-placeholder,input[type='time']::-webkit-input-placeholder,input[type='color']::-webkit-input-placeholder,textarea::-webkit-input-placeholder,select::-webkit-input-placeholder{color:#ccc}input[type='email']:-ms-input-placeholder,input[type='number']:-ms-input-placeholder,input[type='password']:-ms-input-placeholder,input[type='search']:-ms-input-placeholder,input[type='tel']:-ms-input-placeholder,input[type='text']:-ms-input-placeholder,input[type='url']:-ms-input-placeholder,input[type='date']:-ms-input-placeholder,input[type='time']:-ms-input-placeholder,input[type='color']:-ms-input-placeholder,textarea:-ms-input-placeholder,select:-ms-input-placeholder{color:#ccc}input[type='email']::-ms-input-placeholder,input[type='number']::-ms-input-placeholder,input[type='password']::-ms-input-placeholder,input[type='search']::-ms-input-placeholder,input[type='tel']::-ms-input-placeholder,input[type='text']::-ms-input-placeholder,input[type='url']::-ms-input-placeholder,input[type='date']::-ms-input-placeholder,input[type='time']::-ms-input-placeholder,input[type='color']::-ms-input-placeholder,textarea::-ms-input-placeholder,select::-ms-input-placeholder{color:#ccc}input[type='email']::placeholder,input[type='number']::placeholder,input[type='password']::placeholder,input[type='search']::placeholder,input[type='tel']::placeholder,input[type='text']::placeholder,input[type='url']::placeholder,input[type='date']::placeholder,input[type='time']::placeholder,input[type='color']::placeholder,textarea::placeholder,select::placeholder{color:#ccc}::-moz-selection{color:white;background-color:#00a1cc}::selection{color:white;background-color:#00a1cc}.navbar{background-color:#f7f7f7;-webkit-box-sizing:border-box;box-sizing:border-box;padding:10px 0;margin-bottom:50px;border-bottom:#ccc 0.5px solid}.navbar .navbar__wrapper{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;position:relative}.navbar .navbar__logo{-ms-flex-preferred-size:100px;flex-basis:100px}.navbar .navbar__logo-link{display:inline-block}.navbar .navbar__link-list{margin:0;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-pack:distribute;justify-content:space-around}.navbar .navbar__link{margin:9px;color:#606c76}.navbar .navbar__link:active,.navbar .navbar__link:hover,.navbar .navbar__link:hover:visited{color:#00a1cc}.navbar .navbar__link:visited{color:#606c76}.navbar .navbar__search-box{margin:0 12% 0 15px;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-flex:1;-ms-flex:1;flex:1}.navbar .navbar__search-box>input[type="search"]{border-top-right-radius:0;border-bottom-right-radius:0;margin:0;height:32px;background-color:white !important;width:100%}.navbar .navbar__search-box .navbar__search-dropdown{margin:0;margin-left:-1px;padding:0;padding-left:10px;color:#606c76;-webkit-appearance:auto;-moz-appearance:auto;appearance:auto;background-color:white;height:32px;width:80px;border-top-left-radius:0;border-bottom-left-radius:0}.navbar .navbar__dropdown-btn{display:none;padding:0;margin:0;border:none;background-color:transparent;color:#00a1cc}.navbar .navbar__dropdown-btn:focus,.navbar .navbar__dropdown-btn:hover{background-color:transparent;color:#606c76}@media (max-width: 575.98px){.navbar{padding:2px 0}.navbar .navbar__wrapper{display:block}.navbar .navbar__logo-img{width:72px;margin-right:10px;position:relative;top:7px}.navbar .navbar__link-list{margin-top:7px;max-height:0;-webkit-transition:max-height 0.6s ease-out;transition:max-height 0.6s ease-out;overflow:hidden}.navbar .navbar__dropdown-btn{display:block;position:absolute;right:5px;top:3px;-webkit-transform:scale(0.7);transform:scale(0.7)}.navbar .navbar__dropdown-btn:hover+.navbar__link-list{max-height:500px;-webkit-transition:max-height 0.6s ease-in;transition:max-height 0.6s ease-in}.navbar .navbar__search-box{margin:0;width:46vw}.navbar .navbar__search-box>input[type="search"]{height:26px;padding:4px 6px;width:32vw}.navbar .navbar__search-box .navbar__search-dropdown{cursor:pointer;height:26px;width:80px;padding-left:5px}}@media (max-width: 991.98px){.navbar{margin-bottom:20px}}.grid{margin:0 auto;position:relative;max-width:110rem;padding:0 2.0rem;width:100%}.grid .grid__main{width:70%;float:left;position:relative}.grid .grid__aside{width:26%;float:right;position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:distribute;justify-content:space-around}.grid::after{content:' ';clear:both;display:table}@media (max-width: 575.98px){.grid .grid__aside{-webkit-box-orient:vertical !important;-webkit-box-direction:normal !important;-ms-flex-direction:column !important;flex-direction:column !important}}@media (max-width: 991.98px){.grid .grid__main{width:100%;float:none}.grid .grid__aside{width:100%;float:none;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.grid .grid__aside--tablet-column{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.grid--reverse-order{-webkit-transform:scaleY(-1);transform:scaleY(-1)}.grid .grid__main--reverse-order{-webkit-transform:scaleY(-1);transform:scaleY(-1)}.grid .grid__aside--reverse-order{-webkit-transform:scaleY(-1);transform:scaleY(-1)}}.pagination{text-align:center;width:100%}.pagination .pagination__page-link{font-weight:normal;margin:0 5px}.pagination .pagination__page-link--current{font-weight:bold;font-size:1.2em;color:#606c76}.pagination .pagination__nav-link{font-size:1.4em;margin:0 2px}.pagination .pagination__nav-link--right-margin{margin-right:18px}.pagination .pagination__nav-link--left-margin{margin-left:18px}.pagination .pagination__nav-link--hidden{display:none}@media (max-width: 575.98px){.pagination .pagination__page-link{margin:0 3px}.pagination .pagination__nav-link{font-size:1.4em;margin:0 2px}.pagination .pagination__nav-link--right-margin{margin-right:10px}.pagination .pagination__nav-link--left-margin{margin-left:10px}}#page-wrapper{position:relative;min-height:100vh;z-index:0}#content-wrapper{padding-bottom:160px}.footer{padding-top:0.4em !important;text-align:center;margin-bottom:4px !important;position:absolute !important;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%);bottom:0;width:100%}.footer__border{padding-top:4px;border-top:#f7f7f7 solid 2px}.footer__link{margin:0 12px;white-space:nowrap}@media (max-width: 575.98px){#content-wrapper{padding-bottom:120px}}.icon-lock svg{fill:#ccc;height:12px;position:relative;top:1px;margin-left:3px}.icon-edit svg{fill:#ccc;height:12px;position:relative;top:2px}.icon-save svg{fill:#ccc;height:12px;position:relative;top:2px}.icon-cross svg{fill:#ccc;height:10px;position:relative}.icon-arrow svg{fill:#606c76;height:15px;position:relative;top:3px}.spinner{display:inline-block;position:relative;left:50%;-webkit-transform:translateX(-50%) scale(0.4);transform:translateX(-50%) scale(0.4);width:80px;height:80px}.spinner div{-webkit-transform-origin:40px 40px;transform-origin:40px 40px;-webkit-animation:spinner 1.2s linear infinite;animation:spinner 1.2s linear infinite}.spinner div::after{content:" ";display:block;position:absolute;top:3px;left:37px;width:6px;height:18px;border-radius:20%;background:#606c76}.spinner div:nth-child(1){-webkit-transform:rotate(0deg);transform:rotate(0deg);-webkit-animation-delay:-1.1s;animation-delay:-1.1s}.spinner div:nth-child(2){-webkit-transform:rotate(30deg);transform:rotate(30deg);-webkit-animation-delay:-1s;animation-delay:-1s}.spinner div:nth-child(3){-webkit-transform:rotate(60deg);transform:rotate(60deg);-webkit-animation-delay:-.9s;animation-delay:-.9s}.spinner div:nth-child(4){-webkit-transform:rotate(90deg);transform:rotate(90deg);-webkit-animation-delay:-.8s;animation-delay:-.8s}.spinner div:nth-child(5){-webkit-transform:rotate(120deg);transform:rotate(120deg);-webkit-animation-delay:-.7s;animation-delay:-.7s}.spinner div:nth-child(6){-webkit-transform:rotate(150deg);transform:rotate(150deg);-webkit-animation-delay:-.6s;animation-delay:-.6s}.spinner div:nth-child(7){-webkit-transform:rotate(180deg);transform:rotate(180deg);-webkit-animation-delay:-.5s;animation-delay:-.5s}.spinner div:nth-child(8){-webkit-transform:rotate(210deg);transform:rotate(210deg);-webkit-animation-delay:-.4s;animation-delay:-.4s}.spinner div:nth-child(9){-webkit-transform:rotate(240deg);transform:rotate(240deg);-webkit-animation-delay:-.3s;animation-delay:-.3s}.spinner div:nth-child(10){-webkit-transform:rotate(270deg);transform:rotate(270deg);-webkit-animation-delay:-.2s;animation-delay:-.2s}.spinner div:nth-child(11){-webkit-transform:rotate(300deg);transform:rotate(300deg);-webkit-animation-delay:-.1s;animation-delay:-.1s}.spinner div:nth-child(12){-webkit-transform:rotate(330deg);transform:rotate(330deg);-webkit-animation-delay:0s;animation-delay:0s}@-webkit-keyframes spinner{0%{opacity:1}100%{opacity:0}}@keyframes spinner{0%{opacity:1}100%{opacity:0}}.bg-mask{background-color:black;z-index:1;-webkit-filter:opacity(20%);filter:opacity(20%);position:fixed;width:100%;height:100%;left:0;top:0;display:none}.mark-modal{z-index:2;display:none;position:fixed;width:500px;top:50%;left:50%;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);background-color:#f7f7f7;padding:20px 20px 10px 20px;color:#606c76}.mark-modal .mark-modal__head{margin-bottom:20px}.mark-modal .mark-modal__head::after{content:' ';clear:both;display:table}.mark-modal .mark-modal__title{font-weight:bold;font-size:1.2em;float:left}.mark-modal .mark-modal__close-button{float:right;cursor:pointer}.mark-modal .mark-modal__confirm-button{float:right}.mark-modal input[type="radio"]{margin-right:0}.mark-modal .mark-modal__rating-star{display:inline;float:left;position:relative;left:-3px}.mark-modal .mark-modal__status-radio{float:right}.mark-modal .mark-modal__status-radio ul{margin-bottom:0}.mark-modal .mark-modal__status-radio li,.mark-modal .mark-modal__status-radio label{display:inline}.mark-modal .mark-modal__status-radio input[type="radio"]{position:relative;top:1px}.mark-modal .mark-modal__clear{content:' ';clear:both;display:table}.mark-modal .mark-modal__content-input,.mark-modal form textarea{height:200px;width:100%;margin-top:5px;margin-bottom:5px;resize:vertical}.mark-modal .mark-modal__tag{margin-bottom:20px}.mark-modal .mark-modal__option{margin-bottom:24px}.mark-modal .mark-modal__option::after{content:' ';clear:both;display:table}.mark-modal .mark-modal__visibility-radio{float:left}.mark-modal .mark-modal__visibility-radio ul,.mark-modal .mark-modal__visibility-radio li,.mark-modal .mark-modal__visibility-radio label{display:inline}.mark-modal .mark-modal__visibility-radio label{font-size:normal}.mark-modal .mark-modal__visibility-radio input[type="radio"]{position:relative;top:2px}.mark-modal .mark-modal__share-checkbox{float:right}.mark-modal .mark-modal__share-checkbox input[type="checkbox"]{position:relative;top:2px}.confirm-modal{z-index:2;display:none;position:fixed;width:500px;top:50%;left:50%;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);background-color:#f7f7f7;padding:20px 20px 10px 20px;color:#606c76}.confirm-modal .confirm-modal__head{margin-bottom:20px}.confirm-modal .confirm-modal__head::after{content:' ';clear:both;display:table}.confirm-modal .confirm-modal__title{font-weight:bold;font-size:1.2em;float:left}.confirm-modal .confirm-modal__close-button{float:right;cursor:pointer}.confirm-modal .confirm-modal__confirm-button{float:right}.announcement-modal{z-index:2;display:none;position:fixed;width:500px;top:50%;left:50%;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);background-color:#f7f7f7;padding:20px 20px 10px 20px;color:#606c76}.announcement-modal .announcement-modal__head{margin-bottom:20px}.announcement-modal .announcement-modal__head::after{content:' ';clear:both;display:table}.announcement-modal .announcement-modal__title{font-weight:bold;font-size:1.2em;float:left}.announcement-modal .announcement-modal__close-button{float:right;cursor:pointer}.announcement-modal .announcement-modal__confirm-button{float:right}.announcement-modal .announcement-modal__body{overflow-y:auto;max-height:64vh}.announcement-modal .announcement-modal__body .announcement__title{display:inline-block}.announcement-modal .announcement-modal__body .announcement__datetime{color:#ccc;margin-left:10px}.announcement-modal .announcement-modal__body .announcement__content{word-break:break-all}@media (max-width: 575.98px){.mark-modal,.confirm-modal,.announcement-modal{width:100%}}.source-label{display:inline;background:transparent;border-radius:.3rem;border-style:solid;border-width:.1rem;line-height:1.2rem;font-size:1.1rem;margin:3px;padding:1px 3px;padding-top:2px;font-weight:lighter;letter-spacing:0.1rem;word-break:keep-all;opacity:1;position:relative;top:-1px}.source-label.source-label__in-site{border-color:#00a1cc;color:#00a1cc}.source-label.source-label__douban{border:none;color:#fff;background-color:#319840}.source-label.source-label__spotify{background-color:#1ed760;color:#000;border:none;font-weight:bold}.source-label.source-label__imdb{background-color:#F5C518;color:#121212;border:none;font-weight:bold}.source-label.source-label__steam{background:linear-gradient(30deg, #1387b8, #111d2e);color:white;border:none;font-weight:600;padding-top:2px}.main-section-wrapper{padding:32px 48px 32px 36px;background-color:#f7f7f7;overflow:auto}.main-section-wrapper input,.main-section-wrapper select{width:100%}.entity-list .entity-list__title{margin-bottom:20px}.entity-list .entity-list__entity{display:-webkit-box;display:-ms-flexbox;display:flex;margin-bottom:36px}.entity-list .entity-list__entity::after{content:' ';clear:both;display:table}.entity-list .entity-list__entity-img{-o-object-fit:contain;object-fit:contain;min-width:130px;max-width:130px}.entity-list .entity-list__entity-text{margin-left:20px;overflow:hidden;width:100%}.entity-list .entity-list__entity-text .tag-collection{margin-left:-3px}.entity-list .entity-list__entity-link{font-size:1.2em}.entity-list .entity-list__entity-title{display:block}.entity-list .entity-list__entity-category{color:#bbb;margin-left:5px;position:relative;top:-1px}.entity-list .entity-list__entity-info{max-width:73%;white-space:nowrap;overflow:hidden;display:inline-block;text-overflow:ellipsis;position:relative;top:0.52em}.entity-list .entity-list__entity-info--full-length{max-width:100%}.entity-list .entity-list__entity-brief{margin-top:8px;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:4;overflow:hidden;margin-bottom:0}.entity-list .entity-list__rating{display:inline-block;margin:0}.entity-list .entity-list__rating--empty{margin-right:5px}.entity-list .entity-list__rating-score{margin-right:5px;position:relative;top:1px}.entity-list .entity-list__rating-star{display:inline;position:relative;top:0.3em;left:-0.3em}.entity-detail .entity-detail__img{height:210px;float:left;-o-object-fit:contain;object-fit:contain;max-width:150px;-o-object-position:top;object-position:top}.entity-detail .entity-detail__img-origin{cursor:-webkit-zoom-in;cursor:zoom-in}.entity-detail .entity-detail__info{float:left;margin-left:20px;overflow:hidden;text-overflow:ellipsis;width:70%}.entity-detail .entity-detail__title{font-weight:bold}.entity-detail .entity-detail__title--secondary{color:#bbb}.entity-detail .entity-detail__fields{display:inline-block;vertical-align:top;width:46%;margin-left:2%}.entity-detail .entity-detail__fields div,.entity-detail .entity-detail__fields span{margin:1px 0}.entity-detail .entity-detail__fields+.tag-collection{margin-top:5px;margin-left:6px}.entity-detail .entity-detail__rating{position:relative;top:-5px}.entity-detail .entity-detail__rating-star{position:relative;left:-4px;top:3px}.entity-detail .entity-detail__rating-score{font-weight:bold}.entity-detail::after{content:' ';clear:both;display:table}.entity-desc{margin-bottom:28px}.entity-desc .entity-desc__title{display:inline-block;margin-bottom:8px}.entity-desc .entity-desc__content{overflow:hidden}.entity-desc .entity-desc__content--folded{max-height:202px}.entity-desc .entity-desc__unfold-button{display:-webkit-box;display:-ms-flexbox;display:flex;color:#00a1cc;background-color:transparent;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;text-align:center}.entity-desc .entity-desc__unfold-button--hidden{display:none}.entity-marks{margin-bottom:28px}.entity-marks .entity-marks__title{margin-bottom:8px;display:inline-block}.entity-marks .entity-marks__title>a{margin-right:5px}.entity-marks .entity-marks__title--stand-alone{margin-bottom:20px}.entity-marks .entity-marks__more-link{margin-left:5px}.entity-marks .entity-marks__mark{margin:0;padding:3px 0;border-bottom:1px dashed #e5e5e5}.entity-marks .entity-marks__mark:last-child{border:none}.entity-marks .entity-marks__mark--wider{padding:6px 0}.entity-marks .entity-marks__mark-content{margin-bottom:0}.entity-marks .entity-marks__mark-time{color:#ccc;margin-left:2px}.entity-marks .entity-marks__rating-star{position:relative;top:4px}.entity-reviews:first-child{margin-bottom:28px}.entity-reviews .entity-reviews__title{display:inline-block;margin-bottom:8px}.entity-reviews .entity-reviews__title>a{margin-right:5px}.entity-reviews .entity-reviews__title--stand-alone{margin-bottom:20px}.entity-reviews .entity-reviews__more-link{margin-left:5px}.entity-reviews .entity-reviews__review{margin:0;padding:3px 0;border-bottom:1px dashed #e5e5e5}.entity-reviews .entity-reviews__review:last-child{border:none}.entity-reviews .entity-reviews__review--wider{padding:6px 0}.entity-reviews .entity-reviews__review-time{color:#ccc;margin-left:2px}.dividing-line{height:0;width:100%;margin:40px 0 24px 0;border-top:solid 1px #ccc}.dividing-line.dividing-line--dashed{margin:0;margin-top:10px;margin-bottom:2px;border-top:1px dashed #e5e5e5}.entity-sort{position:relative;margin-bottom:30px}.entity-sort .entity-sort__label{font-size:large;display:inline-block;margin-bottom:20px}.entity-sort .entity-sort__more-link{margin-left:5px}.entity-sort .entity-sort__entity-list{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;-ms-flex-wrap:wrap;flex-wrap:wrap}.entity-sort .entity-sort__entity{padding:0 10px;-ms-flex-preferred-size:20%;flex-basis:20%;text-align:center;display:inline-block;color:#606c76}.entity-sort .entity-sort__entity:hover{color:#00a1cc}.entity-sort .entity-sort__entity>a{color:inherit}.entity-sort .entity-sort__entity-img{height:110px}.entity-sort .entity-sort__entity-name{text-overflow:ellipsis;overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.entity-sort--placeholder{border:dashed #bbb 4px}.entity-sort--hover{padding:10px;border:dashed #00a1cc 2px !important;border-radius:3px}.entity-sort--sortable{padding:10px;margin:10px 0;border:dashed #bbb 2px;cursor:all-scroll}.entity-sort--hidden{opacity:0.4}.entity-sort-control{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end}.entity-sort-control__button{margin-top:5px;margin-left:12px;padding:0 2px;cursor:pointer;color:#bbb}.entity-sort-control__button:hover{color:#00a1cc}.entity-sort-control__button:hover>.icon-save svg,.entity-sort-control__button:hover>.icon-edit svg{fill:#00a1cc}.entity-sort-control__button--float-right{position:absolute;top:4px;right:10px}.related-user-list .related-user-list__title{margin-bottom:20px}.related-user-list .related-user-list__user{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;margin-bottom:20px}.related-user-list .related-user-list__user-info{margin-left:15px;overflow:auto}.related-user-list .related-user-list__user-avatar{max-height:72px;min-width:72px}.review-head .review-head__title{display:inline-block;font-weight:bold}.review-head .review-head__body{margin-bottom:10px}.review-head .review-head__body::after{content:' ';clear:both;display:table}.review-head .review-head__info{float:left}.review-head .review-head__owner-link{color:#ccc}.review-head .review-head__owner-link:hover{color:#00a1cc}.review-head .review-head__time{color:#ccc}.review-head .review-head__rating-star{position:relative;top:3px;left:-1px}.review-head .review-head__actions{float:right}.review-head .review-head__action-link:not(:first-child){margin-left:5px}.tag-collection{margin-left:-9px}.tag-collection .tag-collection__tag{position:relative;display:block;float:left;color:white;background:#ccc;padding:5px;border-radius:.3rem;line-height:1.2em;font-size:80%;margin:3px}.tag-collection .tag-collection__tag a{color:white}.tag-collection .tag-collection__tag a:hover{color:#00a1cc}.track-carousel{position:relative;margin-top:5px}.track-carousel__content{overflow:auto;scroll-behavior:smooth;scrollbar-width:none;display:-webkit-box;display:-ms-flexbox;display:flex;margin:auto;-webkit-box-sizing:border-box;box-sizing:border-box;padding-bottom:10px}.track-carousel__content::-webkit-scrollbar{height:3px;width:1px;background-color:#e5e5e5}.track-carousel__content::-webkit-scrollbar-thumb{background-color:#bbb}.track-carousel__track{text-align:center;overflow:hidden;text-overflow:ellipsis;min-width:18%;max-width:18%;margin-right:2.5%}.track-carousel__track img{-o-object-fit:contain;object-fit:contain}.track-carousel__track-title{white-space:nowrap}.track-carousel__button{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-ms-flex-line-pack:center;align-content:center;background:white;border:none;padding:8px;border-radius:50%;outline:0;cursor:pointer;position:absolute;top:50%}.track-carousel__button--prev{left:0;-webkit-transform:translate(50%, -50%);transform:translate(50%, -50%)}.track-carousel__button--next{right:0;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%)}@media (max-width: 575.98px){.entity-list .entity-list__entity{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin-bottom:30px}.entity-list .entity-list__entity-text{margin-left:0}.entity-list .entity-list__entity-img-wrapper{margin-bottom:8px}.entity-list .entity-list__entity-info{max-width:unset}.entity-list .entity-list__rating--empty+.entity-list__entity-info{max-width:70%}.entity-list .entity-list__entity-brief{-webkit-line-clamp:5}.entity-detail{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.entity-detail .entity-detail__title{margin-bottom:5px}.entity-detail .entity-detail__info{margin-left:0;float:none;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;width:100%}.entity-detail .entity-detail__img{margin-bottom:24px;float:none;height:unset;max-width:170px}.entity-detail .entity-detail__fields{width:unset;margin-left:unset}.entity-detail .entity-detail__fields+.tag-collection{margin-left:-3px}.dividing-line{margin-top:24px}.entity-sort .entity-sort__entity{-ms-flex-preferred-size:50%;flex-basis:50%}.entity-sort .entity-sort__entity-img{height:130px}.review-head .review-head__info{float:unset}.review-head .review-head__actions{float:unset}.track-carousel__content{padding-bottom:10px}.track-carousel__track{min-width:31%;max-width:31%;margin-right:4.5%}}@media (max-width: 991.98px){.main-section-wrapper{padding:32px 28px 28px 28px}.entity-detail{display:-webkit-box;display:-ms-flexbox;display:flex}}.aside-section-wrapper{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-ms-flex:1;flex:1;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;width:100%;padding:28px 25px 12px 25px;background-color:#f7f7f7;margin-bottom:30px;overflow:auto}.aside-section-wrapper--transparent{background-color:unset}.aside-section-wrapper--collapse{padding:unset}.add-entity-entries .add-entity-entries__entry{margin-bottom:10px}.add-entity-entries .add-entity-entries__label{font-size:1.2em;margin-bottom:8px}.add-entity-entries .add-entity-entries__button{line-height:unset;height:unset;padding:4px 15px;margin:5px}.action-panel{margin-bottom:20px}.action-panel .action-panel__label{font-weight:bold;margin-bottom:12px}.action-panel .action-panel__button-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.action-panel .action-panel__button-group--center{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.action-panel .action-panel__button{line-height:unset;height:unset;padding:4px 15px;margin:0 5px}.mark-panel{margin-bottom:20px}.mark-panel .mark-panel__status{font-weight:bold}.mark-panel .mark-panel__rating-star{position:relative;top:2px}.mark-panel .mark-panel__actions{float:right}.mark-panel .mark-panel__actions form{display:inline}.mark-panel .mark-panel__time{color:#ccc;margin-bottom:10px}.mark-panel .mark-panel__clear{content:' ';clear:both;display:table}.review-panel .review-panel__label{font-weight:bold}.review-panel .review-panel__actions{float:right}.review-panel .review-panel__time{color:#ccc;margin-bottom:10px}.review-panel .review-panel__review-title{display:block;margin-bottom:15px;font-weight:bold}.review-panel .review-panel__clear{content:' ';clear:both;display:table}.user-profile .user-profile__header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;margin-bottom:15px}.user-profile .user-profile__avatar{width:72px}.user-profile .user-profile__username{font-size:large;margin-left:10px;margin-bottom:0}.user-profile .user-profile__report-link{color:#ccc}.user-relation .user-relation__label{display:inline-block;font-size:large;margin-bottom:10px}.user-relation .user-relation__more-link{margin-left:5px}.user-relation .user-relation__related-user-list{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.user-relation .user-relation__related-user{-ms-flex-preferred-size:25%;flex-basis:25%;padding:0px 3px;text-align:center;display:inline-block;overflow:hidden}.user-relation .user-relation__related-user>a:hover{color:#606c76}.user-relation .user-relation__related-user-avatar{width:48px}.user-relation .user-relation__related-user-name{color:inherit;overflow:hidden;text-overflow:ellipsis;-webkit-box-orient:vertical;-webkit-line-clamp:2}.report-panel .report-panel__label{display:inline-block;margin-bottom:10px}.report-panel .report-panel__report{margin:2px 0}.report-panel .report-panel__user-link{margin:0 2px}.report-panel .report-panel__all-link{margin-left:5px}.relation-dropdown .relation-dropdown__button{display:none}.entity-card{display:-webkit-box;display:-ms-flexbox;display:flex;margin-bottom:10px;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.entity-card--horizontal{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.entity-card .entity-card__img{height:150px}.entity-card .entity-card__rating-star{position:relative;top:4px;left:-3px}.entity-card .entity-card__rating-score{position:relative;top:1px;margin-left:2px}.entity-card .entity-card__title{margin-bottom:10px;margin-top:5px}.entity-card .entity-card__info-wrapper--horizontal{margin-left:20px}.entity-card .entity-card__img-wrapper{-ms-flex-preferred-size:100px;flex-basis:100px}@media (max-width: 575.98px){.add-entity-entries{display:block !important}.add-entity-entries .add-entity-entries__button{width:100%;margin:5px 0 5px 0}.aside-section-wrapper:first-child{margin-right:0 !important;margin-bottom:0 !important}.aside-section-wrapper--singular:first-child{margin-bottom:20px !important}.action-panel{-webkit-box-orient:vertical !important;-webkit-box-direction:normal !important;-ms-flex-direction:column !important;flex-direction:column !important}.entity-card--horizontal{-webkit-box-orient:vertical !important;-webkit-box-direction:normal !important;-ms-flex-direction:column !important;flex-direction:column !important}.entity-card .entity-card__info-wrapper{margin-left:10px !important}.entity-card .entity-card__info-wrapper--horizontal{margin-left:0 !important}}@media (max-width: 991.98px){.add-entity-entries{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-pack:distribute;justify-content:space-around}.aside-section-wrapper{padding:24px 25px 10px 25px;margin-top:20px}.aside-section-wrapper:not(:last-child){margin-right:20px}.aside-section-wrapper--collapse{padding:24px 25px 10px 25px !important;margin-top:0;margin-bottom:0}.aside-section-wrapper--collapse:first-child{margin-right:0}.aside-section-wrapper--no-margin{margin:0}.action-panel{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.action-panel .action-panel__button-group{-webkit-box-pack:space-evenly;-ms-flex-pack:space-evenly;justify-content:space-evenly}.relation-dropdown{margin-bottom:20px}.relation-dropdown .relation-dropdown__button{padding-bottom:10px;background-color:#f7f7f7;width:100%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;cursor:pointer}.relation-dropdown .relation-dropdown__button:focus{background-color:red}.relation-dropdown .relation-dropdown__button>.icon-arrow{-webkit-transition:-webkit-transform 0.3s;transition:-webkit-transform 0.3s;transition:transform 0.3s;transition:transform 0.3s, -webkit-transform 0.3s}.relation-dropdown .relation-dropdown__button:hover>.icon-arrow>svg{fill:#00a1cc}.relation-dropdown .relation-dropdown__button>.icon-arrow--expand{-webkit-transform:rotate(-180deg);transform:rotate(-180deg)}.relation-dropdown .relation-dropdown__button+.relation-dropdown__body--expand{max-height:500px;-webkit-transition:max-height 0.6s ease-in;transition:max-height 0.6s ease-in}.relation-dropdown .relation-dropdown__body{background-color:#f7f7f7;max-height:0;-webkit-transition:max-height 0.6s ease-out;transition:max-height 0.6s ease-out;overflow:hidden}.entity-card{-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.entity-card .entity-card__info-wrapper{margin-left:30px}}.single-section-wrapper{padding:32px 36px;background-color:#f7f7f7;overflow:auto}.single-section-wrapper .single-section-wrapper__link--secondary{display:inline-block;color:#ccc;margin-bottom:20px}.single-section-wrapper .single-section-wrapper__link--secondary:hover{color:#00a1cc}.entity-form,.review-form{overflow:auto}.entity-form>input[type='email'],.entity-form>input[type='number'],.entity-form>input[type='password'],.entity-form>input[type='search'],.entity-form>input[type='tel'],.entity-form>input[type='text'],.entity-form>input[type='url'],.entity-form textarea,.review-form>input[type='email'],.review-form>input[type='number'],.review-form>input[type='password'],.review-form>input[type='search'],.review-form>input[type='tel'],.review-form>input[type='text'],.review-form>input[type='url'],.review-form textarea{width:100%}.entity-form img,.review-form img{display:block}.review-form .review-form__preview-button{color:#00a1cc;font-weight:bold;cursor:pointer}.review-form .review-form__fyi{color:#ccc}.review-form .review-form__main-content,.review-form textarea{margin-bottom:5px;resize:vertical;height:400px}.review-form .review-form__option{margin-top:24px;margin-bottom:10px}.review-form .review-form__option::after{content:' ';clear:both;display:table}.review-form .review-form__visibility-radio{float:left}.review-form .review-form__visibility-radio ul,.review-form .review-form__visibility-radio li,.review-form .review-form__visibility-radio label{display:inline}.review-form .review-form__visibility-radio label{font-size:normal}.review-form .review-form__visibility-radio input[type="radio"]{position:relative;top:2px}.review-form .review-form__share-checkbox{float:right}.review-form .review-form__share-checkbox input[type="checkbox"]{position:relative;top:2px}.report-form input,.report-form select{width:100%}@media (max-width: 575.98px){.review-form .review-form__visibility-radio{float:unset}.review-form .review-form__share-checkbox{float:unset;position:relative;left:-3px}}.markdownx-preview{min-height:100px}.markdownx-preview ul li{list-style:circle inside}.rating-star .jq-star{cursor:unset !important}.ms-parent>.ms-choice{margin-bottom:1.5rem;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:transparent;border:0.1rem solid #ccc;border-radius:.4rem;-webkit-box-shadow:none;box-shadow:none;-webkit-box-sizing:inherit;box-sizing:inherit;padding:.6rem 1.0rem;width:100%;height:30.126px}.ms-parent>.ms-choice:focus{border-color:#00a1cc}.ms-parent>.ms-choice>.icon-caret{top:15.5px}.ms-parent>.ms-choice>span{color:black;font-weight:initial;font-size:13.3333px;top:2.5px;left:2px}.ms-parent>.ms-choice>span:hover,.ms-parent>.ms-choice>span:focus{color:black}.ms-parent>.ms-drop>ul>li>label>span{margin-left:10px}.ms-parent>.ms-drop>ul>li>label>input{width:unset}
diff --git a/common/static/sass/_Label.sass b/common/static/sass/_Label.sass
index 1fd97c94..e80cad4b 100644
--- a/common/static/sass/_Label.sass
+++ b/common/static/sass/_Label.sass
@@ -7,6 +7,8 @@ $spotify-color-primary: #1ed760
$spotify-color-secondary: black
$imdb-color-primary: #F5C518
$imdb-color-secondary: #121212
+$steam-color-primary: #1387b8
+$steam-color-secondary: #111d2e
.source-label
display: inline
@@ -46,3 +48,9 @@ $imdb-color-secondary: #121212
color: $imdb-color-secondary
border: none
font-weight: bold
+ &.source-label__steam
+ background: linear-gradient(30deg, $steam-color-primary, $steam-color-secondary)
+ color: white
+ border: none
+ font-weight: 600
+ padding-top: 2px
diff --git a/common/templates/common/search_result.html b/common/templates/common/search_result.html
index 85e08973..baec6b0d 100644
--- a/common/templates/common/search_result.html
+++ b/common/templates/common/search_result.html
@@ -65,7 +65,7 @@
{% endif %}
- {% if not request.GET.c or request.GET.c != 'movie' and request.GET.c != 'book'%}
+ {% if not request.GET.c or not request.GET.c in categories %}
[{{item.verbose_category_name}}]
{% endif %}
@@ -161,7 +161,7 @@
{% endif %}
- {% if not request.GET.c or request.GET.c != 'movie' and request.GET.c != 'book'%}
+ {% if not request.GET.c or not request.GET.c in categories %}
[{{item.verbose_category_name}}]
{% endif %}
@@ -221,6 +221,86 @@
{% endwith %}
+ {% elif item.category_name|lower == 'game' %}
+
+ {% with game=item %}
+
+
+
+
+
+ {% if game.rating %}
+
+
{{ game.rating }}
+ {% else %}
+
{% trans '暂无评分' %}
+ {% endif %}
+
+
+
+ {% if game.other_title %}{% trans '别名' %}
+ {% for other_title in game.other_title %}
+ {{ other_title }}{% if not forloop.last %} {% endif %}
+ {% endfor %}/
+ {% endif %}
+
+ {% if game.developer %}{% trans '开发商' %}
+ {% for developer in game.developer %}
+ {{ developer }}{% if not forloop.last %} {% endif %}
+ {% endfor %}/
+ {% endif %}
+
+ {% if game.genre %}{% trans '类型' %}
+ {% for genre in game.genre %}
+ {{ genre }}{% if not forloop.last %} {% endif %}
+ {% endfor %}/
+ {% endif %}
+
+ {% if game.platform %}{% trans '平台' %}
+ {% for platform in game.platform %}
+ {{ platform }}{% if not forloop.last %} {% endif %}
+ {% endfor %}/
+ {% endif %}
+
+
+
+ {{ game.brief }}
+
+
+ {% for tag_dict in game.tag_list %}
+ {% for k, v in tag_dict.items %}
+ {% if k == 'content' %}
+
+ {{ v }}
+
+ {% endif %}
+ {% endfor %}
+ {% endfor %}
+
+
+
+
+ {% endwith %}
+
{% elif item.category_name|lower == 'album' or item.category_name|lower == 'song' %}
{% with music=item %}
@@ -260,7 +340,7 @@
{% endif %}
- {% if not request.GET.c or request.GET.c != 'music' and request.GET.c != 'book' and request.GET.c != 'music' %}
+ {% if not request.GET.c or not request.GET.c in categories %}
[{{item.verbose_category_name}}]
{% endif %}
@@ -371,7 +451,7 @@
{% trans '没有想要的结果?' %}
- {% if request.GET.c %}
+ {% if request.GET.c and request.GET.c in categories %}
{% if request.GET.c|lower == 'book' %}
@@ -387,17 +467,6 @@
{% elif request.GET.c|lower == 'music' %}
-
- {% trans '添加音乐' %}
-
-
- {% else %}
-
- {% trans '添加书' %}
-
-
- {% trans '添加电影/剧集' %}
-
{% trans '添加专辑' %}
@@ -405,6 +474,12 @@
{% trans '添加单曲' %}
+ {% elif request.GET.c|lower == 'game' %}
+
+
+ {% trans '添加游戏' %}
+
+
{% endif %}
{% else %}
@@ -420,10 +495,13 @@
{% trans '添加单曲' %}
+
+ {% trans '添加游戏' %}
+
{% endif %}
+
+ {% include "partial/_navbar.html" %}
+
+
+
+ {% include "partial/_footer.html" %}
+
+
+
+ {% comment %}
+
{% oauth_token %}
+
{% mastodon request.user.mastodon_site %}
+
+
{{ user.mastodon_id }}
+ {% endcomment %}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/games/templates/games/create_update_review.html b/games/templates/games/create_update_review.html
new file mode 100644
index 00000000..54391b6d
--- /dev/null
+++ b/games/templates/games/create_update_review.html
@@ -0,0 +1,130 @@
+{% load static %}
+{% load i18n %}
+{% load l10n %}
+{% load humanize %}
+{% load admin_url %}
+{% load mastodon %}
+{% load oauth_token %}
+{% load truncate %}
+{% load thumb %}
+
+
+
+
+
+
+
{% trans 'NiceDB - ' %}{{ title }}
+
+
+
+
+
+
+
+
+
+
+ {% include "partial/_navbar.html" %}
+
+
+
+
+
+
+
+
+
+
+
+ {% if game.genre %}{% trans '类型:' %}
+ {% for genre in game.genre %}
+ {{ genre }} {% if not forloop.last %} / {% endif %}
+ {% endfor %}
+ {% endif %}
+
+
+
+ {% if game.developer %}{% trans '开发商:' %}
+ {% for developer in game.developer %}
+ {{ developer }} {% if not forloop.last %} / {% endif %}
+ {% endfor %}
+ {% endif %}
+
+
+
{% if game.release_date %}
+ {% trans '发行日期:' %}{{ game.release_date }}
+ {% endif %}
+
+
+ {% if game.rating %}
+ {% trans '评分:' %}
+
{{ game.rating }}
+ {% endif %}
+
+
+
+
+
+
+
+
+
+
+
+ {% include "partial/_footer.html" %}
+
+
+ {% comment %}
+
{% oauth_token %}
+
{% mastodon request.user.mastodon_site %}
+
+
{{ user.mastodon_id }}
+ {% endcomment %}
+
+
+
+
+
+
diff --git a/games/templates/games/delete.html b/games/templates/games/delete.html
new file mode 100644
index 00000000..008a561b
--- /dev/null
+++ b/games/templates/games/delete.html
@@ -0,0 +1,105 @@
+{% load static %}
+{% load i18n %}
+{% load l10n %}
+{% load humanize %}
+{% load admin_url %}
+{% load mastodon %}
+{% load oauth_token %}
+{% load truncate %}
+{% load thumb %}
+
+
+
+
+
+
+
{% trans 'NiceDB - 删除电影/剧集' %}
+
+
+
+
+
+
+
+
+
+
+ {% include "partial/_navbar.html" %}
+
+
+
+
+
{% trans '确认删除这个游戏吗?相关评论和标记将一并删除。' %}
+
+
+
+
+
+
+
+ {% if game.rating %}
+ {% trans '评分:' %}
+
+
{{ game.rating }}
+ {% else %}
+
{% trans '评分:暂无评分' %}
+ {% endif %}
+
+ {% if game.last_editor %}
+
+ {% endif %}
+
+
{% trans '上次编辑时间:' %}{{ game.edited_time }}
+
+ {% if game.game_marks.all %}
+
+ {% endif %}
+ {% if game.game_reviews.all %}
+
+ {% endif %}
+
+
+
+
+
+
+ {% trans '返回' %}
+
+
+
+
+
+
+ {% include "partial/_footer.html" %}
+
+
+
+ {% comment %}
+
{% oauth_token %}
+
{% mastodon request.user.mastodon_site %}
+
+
{{ user.mastodon_id }}
+ {% endcomment %}
+
+
+
+
+
+
diff --git a/games/templates/games/delete_review.html b/games/templates/games/delete_review.html
new file mode 100644
index 00000000..b24fc5d2
--- /dev/null
+++ b/games/templates/games/delete_review.html
@@ -0,0 +1,107 @@
+{% load static %}
+{% load i18n %}
+{% load admin_url %}
+{% load mastodon %}
+{% load oauth_token %}
+{% load truncate %}
+
+
+
+
+
+
+
{% trans 'NiceDB - 删除评论' %}
+
+
+
+
+
+
+
+
+
+ {% include "partial/_navbar.html" %}
+
+
+
+
+
{% trans '确认删除这篇评论吗?' %}
+
+
+
+
+
+
+
+ {{ review.title }}
+
+ {% if review.is_private %}
+
+
+
+ {% endif %}
+
+
+
+
+
{{ review.owner.username }}
+
+ {% if mark %}
+
+ {% if mark.rating %}
+
+ {% endif %}
+
+ {% endif %}
+
+
{{ review.edited_time }}
+
+
+
+
+
+
+
+ {{ form.content }}
+
+ {{ form.media }}
+
+
+
+
+
+ {% trans '返回' %}
+
+
+
+
+
+
+ {% include "partial/_footer.html" %}
+
+
+
+ {% comment %}
+
{% oauth_token %}
+
{% mastodon request.user.mastodon_site %}
+
+
{{ user.mastodon_id }}
+ {% endcomment %}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/games/templates/games/detail.html b/games/templates/games/detail.html
new file mode 100644
index 00000000..a5b18811
--- /dev/null
+++ b/games/templates/games/detail.html
@@ -0,0 +1,420 @@
+{% load static %}
+{% load i18n %}
+{% load l10n %}
+{% load humanize %}
+{% load admin_url %}
+{% load mastodon %}
+{% load oauth_token %}
+{% load truncate %}
+{% load strip_scheme %}
+{% load thumb %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{% trans 'NiceDB - 游戏详情' %} | {{ game.title }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {% include "partial/_navbar.html" %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% if game.rating %}
+
+ {{ game.rating }}
+ {% else %}
+ {% trans '评分:暂无评分' %}
+ {% endif %}
+
+
+
{% if game.other_title %}{% trans '别名:' %}
+ {% for other_title in game.other_title %}
+
5 %}style="display: none;" {% endif %}>
+ {{ other_title }}
+ {% if not forloop.last %} / {% endif %}
+
+ {% endfor %}
+ {% if game.other_title|length > 5 %}
+
{% trans '更多' %}
+
+ {% endif %}
+ {% endif %}
+
+
+
+ {% if game.genre %}{% trans '类型:' %}
+ {% for genre in game.genre %}
+ {{ genre }} {% if not forloop.last %} / {% endif %}
+ {% endfor %}
+ {% endif %}
+
+
+
+ {% if game.developer %}{% trans '开发商:' %}
+ {% for developer in game.developer %}
+ {{ developer }} {% if not forloop.last %} / {% endif %}
+ {% endfor %}
+ {% endif %}
+
+
+
+ {% if game.publisher %}{% trans '发行商:' %}
+ {% for publisher in game.publisher %}
+ {{ publisher }} {% if not forloop.last %} / {% endif %}
+ {% endfor %}
+ {% endif %}
+
+
+
+
+
{% if game.release_date %}
+ {% trans '发行日期:' %}{{ game.release_date }}
+ {% endif %}
+
+
+ {% if game.platform %}{% trans '平台:' %}
+ {% for platform in game.platform %}
+ {{ platform }} {% if not forloop.last %} / {% endif %}
+ {% endfor %}
+ {% endif %}
+
+
+ {% if game.other_info %}
+ {% for k, v in game.other_info.items %}
+
+ {{ k }}:{{ v | urlize }}
+
+ {% endfor %}
+ {% endif %}
+
+
+ {% if game.last_editor %}
+
+ {% endif %}
+
+
+
+
+
+
+ {% for tag_dict in game_tag_list %}
+ {% for k, v in tag_dict.items %}
+ {% if k == 'content' %}
+
+ {{ v }}
+
+ {% endif %}
+ {% endfor %}
+ {% endfor %}
+
+
+
+
+
+ {% if game.brief %}
+
+
{% trans '简介' %}
+
+
{{ game.brief | linebreaksbr }}
+
+
+
+ {% endif %}
+
+
+
+
+
{% trans '这个游戏的标记' %}
+ {% if mark_list_more %}
+
{% trans '更多' %}
+ {% endif %}
+ {% if mark_list %}
+
+ {% for others_mark in mark_list %}
+
+ {{ others_mark.owner.username }}
+ {{ others_mark.get_status_display }}
+ {% if others_mark.rating %}
+
+ {% endif %}
+ {% if others_mark.is_private %}
+
+ {% endif %}
+ {{ others_mark.edited_time }}
+ {% if others_mark.text %}
+ {{ others_mark.text }}
+ {% endif %}
+
+ {% endfor %}
+
+ {% else %}
+
{% trans '暂无标记' %}
+ {% endif %}
+
+
+
{% trans '这个游戏的评论' %}
+
+ {% if review_list_more %}
+
{% trans '更多' %}
+ {% endif %}
+ {% if review_list %}
+
+ {% else %}
+
{% trans '暂无评论' %}
+ {% endif %}
+
+
+
+
+
+
+
+ {% if mark %}
+
+
+
{% trans '我' %}{{ mark.get_status_display }}
+ {% if mark.status == status_enum.DO.value or mark.status == status_enum.COLLECT.value%}
+ {% if mark.rating %}
+
+ {% endif %}
+ {% endif %}
+ {% if mark.is_private %}
+
+ {% endif %}
+
+ {% trans '修改' %}
+
+
+
+
+
{{ mark.edited_time }}
+
+ {% if mark.text %}
+
{{ mark.text }}
+ {% endif %}
+
+
+ {% for tag in mark_tags %}
+ {{ tag }}
+ {% endfor %}
+
+
+
+ {% else %}
+
+
+
{% trans '标记这个游戏' %}
+
+ {% trans '想玩' %}
+ {% trans '在玩' %}
+ {% trans '玩过' %}
+
+
+ {% endif %}
+
+
+
+
+ {% if review %}
+
+ {% else %}
+
+
+
+ {% endif %}
+
+
+
+
+
+
+
+ {% include "partial/_footer.html" %}
+
+
+
+
+
+
+ {% if not mark %}
+
+
+
+
+ {% else %}
+
{% trans '我的标记' %}
+ {% endif %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{% trans '确定要删除你的标记吗?' %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/games/templates/games/mark_list.html b/games/templates/games/mark_list.html
new file mode 100644
index 00000000..45071f89
--- /dev/null
+++ b/games/templates/games/mark_list.html
@@ -0,0 +1,165 @@
+{% load static %}
+{% load i18n %}
+{% load l10n %}
+{% load humanize %}
+{% load admin_url %}
+{% load mastodon %}
+{% load oauth_token %}
+{% load truncate %}
+{% load highlight %}
+{% load thumb %}
+
+
+
+
+
+
+
{% trans 'NiceDB - ' %}{{ game.title }}{% trans '的标记' %}
+
+
+
+
+
+
+
+
+
+
+ {% include "partial/_navbar.html" %}
+
+
+
+
+
+
+
+
+
+ {% for mark in marks %}
+
+
+ {{ mark.owner.username }}
+ {{ mark.get_status_display }}
+ {% if mark.rating %}
+
+ {% endif %}
+ {% if mark.is_private %}
+
+
+
+ {% endif %}
+ {{ mark.edited_time }}
+ {% if mark.text %}
+ {{ mark.text }}
+ {% endif %}
+
+
+ {% empty %}
+
+ {% trans '无结果' %}
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% if game.genre %}{% trans '类型:' %}
+ {% for genre in game.genre %}
+ {{ genre }} {% if not forloop.last %} / {% endif %}
+ {% endfor %}
+ {% endif %}
+
+
+
+ {% if game.developer %}{% trans '开发商:' %}
+ {% for developer in game.developer %}
+ {{ developer }} {% if not forloop.last %} / {% endif %}
+ {% endfor %}
+ {% endif %}
+
+
+
{% if game.release_date %}
+ {% trans '发行日期:' %}{{ game.release_date }}
+ {% endif %}
+
+
+ {% if game.rating %}
+ {% trans '评分:' %}
+
{{ game.rating }}
+ {% endif %}
+
+
+
+
+
+
+
+
+ {% include "partial/_footer.html" %}
+
+
+
+ {% comment %}
+
{% oauth_token %}
+
{% mastodon request.user.mastodon_site %}
+
+
{{ user.mastodon_id }}
+ {% endcomment %}
+
+
+
+
+
+
diff --git a/games/templates/games/review_detail.html b/games/templates/games/review_detail.html
new file mode 100644
index 00000000..1ffab003
--- /dev/null
+++ b/games/templates/games/review_detail.html
@@ -0,0 +1,152 @@
+{% load static %}
+{% load i18n %}
+{% load l10n %}
+{% load humanize %}
+{% load admin_url %}
+{% load mastodon %}
+{% load oauth_token %}
+{% load truncate %}
+{% load thumb %}
+
+
+
+
+
+
+
+
+
+
+
+
{% trans 'NiceDB - 评论详情' %}
+
+
+
+
+
+
+
+
+
+
+ {% include "partial/_navbar.html" %}
+
+
+
+
+
+
+
+ {{ review.title }}
+
+ {% if review.is_private %}
+
+
+
+ {% endif %}
+
+
+
+
{{ review.owner.username }}
+
+ {% if mark %}
+
+ {% if mark.rating %}
+
+ {% endif %}
+
+ {% endif %}
+
+
{{ review.edited_time }}
+
+
+
+
+
+
+ {{ form.content }}
+
+ {{ form.media }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% if game.genre %}{% trans '类型:' %}
+ {% for genre in game.genre %}
+ {{ genre }} {% if not forloop.last %} / {% endif %}
+ {% endfor %}
+ {% endif %}
+
+
+
+ {% if game.developer %}{% trans '开发商:' %}
+ {% for developer in game.developer %}
+ {{ developer }} {% if not forloop.last %} / {% endif %}
+ {% endfor %}
+ {% endif %}
+
+
+
{% if game.release_date %}
+ {% trans '发行日期:' %}{{ game.release_date }}
+ {% endif %}
+
+
+ {% if game.rating %}
+ {% trans '评分:' %}
+
{{ game.rating }}
+ {% endif %}
+
+
+
+
+
+
+
+
+ {% include "partial/_footer.html" %}
+
+
+
+ {% comment %}
+
{% oauth_token %}
+
{% mastodon request.user.mastodon_site %}
+
+
{{ user.mastodon_id }}
+ {% endcomment %}
+
+
+
+
+
+
diff --git a/games/templates/games/review_list.html b/games/templates/games/review_list.html
new file mode 100644
index 00000000..10b01d43
--- /dev/null
+++ b/games/templates/games/review_list.html
@@ -0,0 +1,153 @@
+{% load static %}
+{% load i18n %}
+{% load l10n %}
+{% load humanize %}
+{% load admin_url %}
+{% load mastodon %}
+{% load oauth_token %}
+{% load truncate %}
+{% load highlight %}
+{% load thumb %}
+
+
+
+
+
+
+
{% trans 'NiceDB - ' %}{{ game.title }}{% trans '的评论' %}
+
+
+
+
+
+
+
+
+
+
+ {% include "partial/_navbar.html" %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% if game.genre %}{% trans '类型:' %}
+ {% for genre in game.genre %}
+ {{ genre }} {% if not forloop.last %} / {% endif %}
+ {% endfor %}
+ {% endif %}
+
+
+
+ {% if game.developer %}{% trans '开发商:' %}
+ {% for developer in game.developer %}
+ {{ developer }} {% if not forloop.last %} / {% endif %}
+ {% endfor %}
+ {% endif %}
+
+
+
{% if game.release_date %}
+ {% trans '发行日期:' %}{{ game.release_date }}
+ {% endif %}
+
+
+ {% if game.rating %}
+ {% trans '评分:' %}
+
{{ game.rating }}
+ {% endif %}
+
+
+
+
+
+
+
+
+ {% include "partial/_footer.html" %}
+
+
+
+ {% comment %}
+
{% oauth_token %}
+
{% mastodon request.user.mastodon_site %}
+
+
{{ user.mastodon_id }}
+ {% endcomment %}
+
+
+
+
+
+
diff --git a/games/templates/games/scrape.html b/games/templates/games/scrape.html
new file mode 100644
index 00000000..87f2efa8
--- /dev/null
+++ b/games/templates/games/scrape.html
@@ -0,0 +1,112 @@
+{% load static %}
+{% load i18n %}
+{% load admin_url %}
+{% load mastodon %}
+{% load oauth_token %}
+{% load truncate %}
+
+
+
+
+
+
+
{% trans 'NiceDB - 从豆瓣获取数据' %}
+
+
+
+
+
+
+
+
+
+ {% include "partial/_navbar.html" %}
+
+
+
+
+
+
+
+ {% trans '根据豆瓣内容填写下方表单' %}
+
+
+
+
+
+
+
+
+
+
+
+ {% trans '复制详情页链接' %}
+
+
+
+
+
+
+
+
+
+ {% include "partial/_footer.html" %}
+
+
+
+
+
+
+
diff --git a/games/tests.py b/games/tests.py
new file mode 100644
index 00000000..7ce503c2
--- /dev/null
+++ b/games/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/games/urls.py b/games/urls.py
new file mode 100644
index 00000000..ff984632
--- /dev/null
+++ b/games/urls.py
@@ -0,0 +1,23 @@
+from django.urls import path
+from .views import *
+
+
+app_name = 'games'
+urlpatterns = [
+ path('create/', create, name='create'),
+ path('
/', retrieve, name='retrieve'),
+ path('update//', update, name='update'),
+ path('delete//', delete, name='delete'),
+ path('mark/', create_update_mark, name='create_update_mark'),
+ path('/mark/list/',
+ retrieve_mark_list, name='retrieve_mark_list'),
+ path('mark/delete//', delete_mark, name='delete_mark'),
+ path('/review/create/', create_review, name='create_review'),
+ path('review/update//', update_review, name='update_review'),
+ path('review/delete//', delete_review, name='delete_review'),
+ path('review//', retrieve_review, name='retrieve_review'),
+ path('/review/list/',
+ retrieve_review_list, name='retrieve_review_list'),
+ path('scrape/', scrape, name='scrape'),
+ path('click_to_scrape/', click_to_scrape, name='click_to_scrape'),
+]
diff --git a/games/views.py b/games/views.py
new file mode 100644
index 00000000..f69e4918
--- /dev/null
+++ b/games/views.py
@@ -0,0 +1,591 @@
+import logging
+from django.shortcuts import render, get_object_or_404, redirect, reverse
+from django.contrib.auth.decorators import login_required, permission_required
+from django.utils.translation import gettext_lazy as _
+from django.http import HttpResponseBadRequest, HttpResponseServerError
+from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
+from django.db import IntegrityError, transaction
+from django.db.models import Count
+from django.utils import timezone
+from django.core.paginator import Paginator
+from mastodon import mastodon_request_included
+from mastodon.api import check_visibility, post_toot, TootVisibilityEnum
+from mastodon.utils import rating_to_emoji
+from common.utils import PageLinksGenerator
+from common.views import PAGE_LINK_NUMBER, jump_or_scrape
+from common.models import SourceSiteEnum
+from .models import *
+from .forms import *
+from boofilsic.settings import MASTODON_TAGS
+
+
+logger = logging.getLogger(__name__)
+mastodon_logger = logging.getLogger("django.mastodon")
+
+
+# how many marks showed on the detail page
+MARK_NUMBER = 5
+# how many marks at the mark page
+MARK_PER_PAGE = 20
+# how many reviews showed on the detail page
+REVIEW_NUMBER = 5
+# how many reviews at the mark page
+REVIEW_PER_PAGE = 20
+# max tags on detail page
+TAG_NUMBER = 10
+
+
+# public data
+###########################
+@login_required
+def create(request):
+ if request.method == 'GET':
+ form = GameForm()
+ return render(
+ request,
+ 'games/create_update.html',
+ {
+ 'form': form,
+ 'title': _('添加游戏'),
+ 'submit_url': reverse("games:create"),
+ # provided for frontend js
+ 'this_site_enum_value': SourceSiteEnum.IN_SITE.value,
+ }
+ )
+ elif request.method == 'POST':
+ if request.user.is_authenticated:
+ # only local user can alter public data
+ form = GameForm(request.POST, request.FILES)
+ if form.is_valid():
+ form.instance.last_editor = request.user
+ try:
+ with transaction.atomic():
+ form.save()
+ if form.instance.source_site == SourceSiteEnum.IN_SITE.value:
+ real_url = form.instance.get_absolute_url()
+ form.instance.source_url = real_url
+ form.instance.save()
+ except IntegrityError as e:
+ logger.error(e.__str__())
+ return HttpResponseServerError("integrity error")
+ return redirect(reverse("games:retrieve", args=[form.instance.id]))
+ else:
+ return render(
+ request,
+ 'games/create_update.html',
+ {
+ 'form': form,
+ 'title': _('添加游戏'),
+ 'submit_url': reverse("games:create"),
+ # provided for frontend js
+ 'this_site_enum_value': SourceSiteEnum.IN_SITE.value,
+ }
+ )
+ else:
+ return redirect(reverse("users:login"))
+ else:
+ return HttpResponseBadRequest()
+
+
+@login_required
+def update(request, id):
+ if request.method == 'GET':
+ game = get_object_or_404(Game, pk=id)
+ form = GameForm(instance=game)
+ page_title = _('修改游戏')
+ return render(
+ request,
+ 'games/create_update.html',
+ {
+ 'form': form,
+ 'title': page_title,
+ 'submit_url': reverse("games:update", args=[game.id]),
+ # provided for frontend js
+ 'this_site_enum_value': SourceSiteEnum.IN_SITE.value,
+ }
+ )
+ elif request.method == 'POST':
+ game = get_object_or_404(Game, pk=id)
+ form = GameForm(request.POST, request.FILES, instance=game)
+ page_title = _("修改游戏")
+ if form.is_valid():
+ form.instance.last_editor = request.user
+ form.instance.edited_time = timezone.now()
+ try:
+ with transaction.atomic():
+ form.save()
+ if form.instance.source_site == SourceSiteEnum.IN_SITE.value:
+ real_url = form.instance.get_absolute_url()
+ form.instance.source_url = real_url
+ form.instance.save()
+ except IntegrityError as e:
+ logger.error(e.__str__())
+ return HttpResponseServerError("integrity error")
+ else:
+ return render(
+ request,
+ 'games/create_update.html',
+ {
+ 'form': form,
+ 'title': page_title,
+ 'submit_url': reverse("games:update", args=[game.id]),
+ # provided for frontend js
+ 'this_site_enum_value': SourceSiteEnum.IN_SITE.value,
+ }
+ )
+ return redirect(reverse("games:retrieve", args=[form.instance.id]))
+
+ else:
+ return HttpResponseBadRequest()
+
+
+@mastodon_request_included
+# @login_required
+def retrieve(request, id):
+ if request.method == 'GET':
+ game = get_object_or_404(Game, pk=id)
+ mark = None
+ mark_tags = None
+ review = None
+
+ # retreive tags
+ game_tag_list = game.game_tags.values('content').annotate(
+ tag_frequency=Count('content')).order_by('-tag_frequency')[:TAG_NUMBER]
+
+ # retrieve user mark and initialize mark form
+ try:
+ if request.user.is_authenticated:
+ mark = GameMark.objects.get(owner=request.user, game=game)
+ except ObjectDoesNotExist:
+ mark = None
+ if mark:
+ mark_tags = mark.gamemark_tags.all()
+ mark.get_status_display = GameMarkStatusTranslator(mark.status)
+ mark_form = GameMarkForm(instance=mark, initial={
+ 'tags': mark_tags
+ })
+ else:
+ mark_form = GameMarkForm(initial={
+ 'game': game,
+ 'tags': mark_tags
+ })
+
+ # retrieve user review
+ try:
+ if request.user.is_authenticated:
+ review = GameReview.objects.get(
+ owner=request.user, game=game)
+ except ObjectDoesNotExist:
+ review = None
+
+ # retrieve other related reviews and marks
+ if request.user.is_anonymous:
+ # hide all marks and reviews for anonymous user
+ mark_list = None
+ review_list = None
+ mark_list_more = None
+ review_list_more = None
+ else:
+ mark_list = GameMark.get_available(
+ game, request.user, request.session['oauth_token'])
+ review_list = GameReview.get_available(
+ game, request.user, request.session['oauth_token'])
+ mark_list_more = True if len(mark_list) > MARK_NUMBER else False
+ mark_list = mark_list[:MARK_NUMBER]
+ for m in mark_list:
+ m.get_status_display = GameMarkStatusTranslator(m.status)
+ review_list_more = True if len(
+ review_list) > REVIEW_NUMBER else False
+ review_list = review_list[:REVIEW_NUMBER]
+
+ # def strip_html_tags(text):
+ # import re
+ # regex = re.compile('<.*?>')
+ # return re.sub(regex, '', text)
+
+ # for r in review_list:
+ # r.content = strip_html_tags(r.content)
+
+ return render(
+ request,
+ 'games/detail.html',
+ {
+ 'game': game,
+ 'mark': mark,
+ 'review': review,
+ 'status_enum': MarkStatusEnum,
+ 'mark_form': mark_form,
+ 'mark_list': mark_list,
+ 'mark_list_more': mark_list_more,
+ 'review_list': review_list,
+ 'review_list_more': review_list_more,
+ 'game_tag_list': game_tag_list,
+ 'mark_tags': mark_tags,
+ }
+ )
+ else:
+ logger.warning('non-GET method at /games/')
+ return HttpResponseBadRequest()
+
+
+@permission_required("games.delete_game")
+@login_required
+def delete(request, id):
+ if request.method == 'GET':
+ game = get_object_or_404(Game, pk=id)
+ return render(
+ request,
+ 'games/delete.html',
+ {
+ 'game': game,
+ }
+ )
+ elif request.method == 'POST':
+ if request.user.is_staff:
+ # only staff has right to delete
+ game = get_object_or_404(Game, pk=id)
+ game.delete()
+ return redirect(reverse("common:home"))
+ else:
+ raise PermissionDenied()
+ else:
+ return HttpResponseBadRequest()
+
+
+# user owned entites
+###########################
+@mastodon_request_included
+@login_required
+def create_update_mark(request):
+ # check list:
+ # clean rating if is wish
+ # transaction on updating game rating
+ # owner check(guarantee)
+ if request.method == 'POST':
+ pk = request.POST.get('id')
+ old_rating = None
+ old_tags = None
+ if pk:
+ mark = get_object_or_404(GameMark, pk=pk)
+ if request.user != mark.owner:
+ return HttpResponseBadRequest()
+ old_rating = mark.rating
+ old_tags = mark.gamemark_tags.all()
+ # update
+ form = GameMarkForm(request.POST, instance=mark)
+ else:
+ # create
+ form = GameMarkForm(request.POST)
+
+ if form.is_valid():
+ if form.instance.status == MarkStatusEnum.WISH.value:
+ form.instance.rating = None
+ form.cleaned_data['rating'] = None
+ form.instance.owner = request.user
+ form.instance.edited_time = timezone.now()
+ game = form.instance.game
+
+ try:
+ with transaction.atomic():
+ # update game rating
+ game.update_rating(old_rating, form.instance.rating)
+ form.save()
+ # update tags
+ if old_tags:
+ for tag in old_tags:
+ tag.delete()
+ if form.cleaned_data['tags']:
+ for tag in form.cleaned_data['tags']:
+ GameTag.objects.create(
+ content=tag,
+ game=game,
+ mark=form.instance
+ )
+ except IntegrityError as e:
+ logger.error(e.__str__())
+ return HttpResponseServerError("integrity error")
+
+ if form.cleaned_data['share_to_mastodon']:
+ if form.cleaned_data['is_private']:
+ visibility = TootVisibilityEnum.PRIVATE
+ else:
+ visibility = TootVisibilityEnum.UNLISTED
+ url = "https://" + request.get_host() + reverse("games:retrieve",
+ args=[game.id])
+ words = GameMarkStatusTranslator(form.cleaned_data['status']) +\
+ f"《{game.title}》" + \
+ rating_to_emoji(form.cleaned_data['rating'])
+
+ # tags = MASTODON_TAGS % {'category': '书', 'type': '标记'}
+ tags = ''
+ content = words + '\n' + url + '\n' + \
+ form.cleaned_data['text'] + '\n' + tags
+ response = post_toot(request.user.mastodon_site, content, visibility,
+ request.session['oauth_token'])
+ if response.status_code != 200:
+ mastodon_logger.error(
+ f"CODE:{response.status_code} {response.text}")
+ return HttpResponseServerError("publishing mastodon status failed")
+ else:
+ return HttpResponseBadRequest("invalid form data")
+
+ return redirect(reverse("games:retrieve", args=[form.instance.game.id]))
+ else:
+ return HttpResponseBadRequest("invalid method")
+
+
+@mastodon_request_included
+@login_required
+def retrieve_mark_list(request, game_id):
+ if request.method == 'GET':
+ game = get_object_or_404(Game, pk=game_id)
+ queryset = GameMark.get_available(
+ game, request.user, request.session['oauth_token'])
+ paginator = Paginator(queryset, MARK_PER_PAGE)
+ page_number = request.GET.get('page', default=1)
+ marks = paginator.get_page(page_number)
+ marks.pagination = PageLinksGenerator(
+ PAGE_LINK_NUMBER, page_number, paginator.num_pages)
+ for m in marks:
+ m.get_status_display = GameMarkStatusTranslator(m.status)
+ return render(
+ request,
+ 'games/mark_list.html',
+ {
+ 'marks': marks,
+ 'game': game,
+ }
+ )
+ else:
+ return HttpResponseBadRequest()
+
+
+@login_required
+def delete_mark(request, id):
+ if request.method == 'POST':
+ mark = get_object_or_404(GameMark, pk=id)
+ if request.user != mark.owner:
+ return HttpResponseBadRequest()
+ game_id = mark.game.id
+ try:
+ with transaction.atomic():
+ # update game rating
+ mark.game.update_rating(mark.rating, None)
+ mark.delete()
+ except IntegrityError as e:
+ return HttpResponseServerError()
+ return redirect(reverse("games:retrieve", args=[game_id]))
+ else:
+ return HttpResponseBadRequest()
+
+
+@mastodon_request_included
+@login_required
+def create_review(request, game_id):
+ if request.method == 'GET':
+ form = GameReviewForm(initial={'game': game_id})
+ game = get_object_or_404(Game, pk=game_id)
+ return render(
+ request,
+ 'games/create_update_review.html',
+ {
+ 'form': form,
+ 'title': _("添加评论"),
+ 'game': game,
+ 'submit_url': reverse("games:create_review", args=[game_id]),
+ }
+ )
+ elif request.method == 'POST':
+ form = GameReviewForm(request.POST)
+ if form.is_valid():
+ form.instance.owner = request.user
+ form.save()
+ if form.cleaned_data['share_to_mastodon']:
+ if form.cleaned_data['is_private']:
+ visibility = TootVisibilityEnum.PRIVATE
+ else:
+ visibility = TootVisibilityEnum.UNLISTED
+ url = "https://" + request.get_host() + reverse("games:retrieve_review",
+ args=[form.instance.id])
+ words = "发布了关于" + f"《{form.instance.game.title}》" + "的评论"
+ # tags = MASTODON_TAGS % {'category': '书', 'type': '评论'}
+ tags = ''
+ content = words + '\n' + url + \
+ '\n' + form.cleaned_data['title'] + '\n' + tags
+ response = post_toot(request.user.mastodon_site, content, visibility,
+ request.session['oauth_token'])
+ if response.status_code != 200:
+ mastodon_logger.error(
+ f"CODE:{response.status_code} {response.text}")
+ return HttpResponseServerError("publishing mastodon status failed")
+ return redirect(reverse("games:retrieve_review", args=[form.instance.id]))
+ else:
+ return HttpResponseBadRequest()
+ else:
+ return HttpResponseBadRequest()
+
+
+@mastodon_request_included
+@login_required
+def update_review(request, id):
+ if request.method == 'GET':
+ review = get_object_or_404(GameReview, pk=id)
+ if request.user != review.owner:
+ return HttpResponseBadRequest()
+ form = GameReviewForm(instance=review)
+ game = review.game
+ return render(
+ request,
+ 'games/create_update_review.html',
+ {
+ 'form': form,
+ 'title': _("编辑评论"),
+ 'game': game,
+ 'submit_url': reverse("games:update_review", args=[review.id]),
+ }
+ )
+ elif request.method == 'POST':
+ review = get_object_or_404(GameReview, pk=id)
+ if request.user != review.owner:
+ return HttpResponseBadRequest()
+ form = GameReviewForm(request.POST, instance=review)
+ if form.is_valid():
+ form.instance.edited_time = timezone.now()
+ form.save()
+ if form.cleaned_data['share_to_mastodon']:
+ if form.cleaned_data['is_private']:
+ visibility = TootVisibilityEnum.PRIVATE
+ else:
+ visibility = TootVisibilityEnum.UNLISTED
+ url = "https://" + request.get_host() + reverse("games:retrieve_review",
+ args=[form.instance.id])
+ words = "发布了关于" + f"《{form.instance.game.title}》" + "的评论"
+ # tags = MASTODON_TAGS % {'category': '书', 'type': '评论'}
+ tags = ''
+ content = words + '\n' + url + \
+ '\n' + form.cleaned_data['title'] + '\n' + tags
+ response = post_toot(request.user.mastodon_site, content, visibility,
+ request.session['oauth_token'])
+ if response.status_code != 200:
+ mastodon_logger.error(
+ f"CODE:{response.status_code} {response.text}")
+ return HttpResponseServerError("publishing mastodon status failed")
+ return redirect(reverse("games:retrieve_review", args=[form.instance.id]))
+ else:
+ return HttpResponseBadRequest()
+ else:
+ return HttpResponseBadRequest()
+
+
+@login_required
+def delete_review(request, id):
+ if request.method == 'GET':
+ review = get_object_or_404(GameReview, pk=id)
+ if request.user != review.owner:
+ return HttpResponseBadRequest()
+ review_form = GameReviewForm(instance=review)
+ return render(
+ request,
+ 'games/delete_review.html',
+ {
+ 'form': review_form,
+ 'review': review,
+ }
+ )
+ elif request.method == 'POST':
+ review = get_object_or_404(GameReview, pk=id)
+ if request.user != review.owner:
+ return HttpResponseBadRequest()
+ game_id = review.game.id
+ review.delete()
+ return redirect(reverse("games:retrieve", args=[game_id]))
+ else:
+ return HttpResponseBadRequest()
+
+
+@mastodon_request_included
+@login_required
+def retrieve_review(request, id):
+ if request.method == 'GET':
+ review = get_object_or_404(GameReview, pk=id)
+ if not check_visibility(review, request.session['oauth_token'], request.user):
+ msg = _("你没有访问这个页面的权限😥")
+ return render(
+ request,
+ 'common/error.html',
+ {
+ 'msg': msg,
+ }
+ )
+ review_form = GameReviewForm(instance=review)
+ game = review.game
+ try:
+ mark = GameMark.objects.get(owner=review.owner, game=game)
+ mark.get_status_display = GameMarkStatusTranslator(mark.status)
+ except ObjectDoesNotExist:
+ mark = None
+ return render(
+ request,
+ 'games/review_detail.html',
+ {
+ 'form': review_form,
+ 'review': review,
+ 'game': game,
+ 'mark': mark,
+ }
+ )
+ else:
+ return HttpResponseBadRequest()
+
+
+@mastodon_request_included
+@login_required
+def retrieve_review_list(request, game_id):
+ if request.method == 'GET':
+ game = get_object_or_404(Game, pk=game_id)
+ queryset = GameReview.get_available(
+ game, request.user, request.session['oauth_token'])
+ paginator = Paginator(queryset, REVIEW_PER_PAGE)
+ page_number = request.GET.get('page', default=1)
+ reviews = paginator.get_page(page_number)
+ reviews.pagination = PageLinksGenerator(
+ PAGE_LINK_NUMBER, page_number, paginator.num_pages)
+ return render(
+ request,
+ 'games/review_list.html',
+ {
+ 'reviews': reviews,
+ 'game': game,
+ }
+ )
+ else:
+ return HttpResponseBadRequest()
+
+
+@login_required
+def scrape(request):
+ if request.method == 'GET':
+ keywords = request.GET.get('q')
+ form = GameForm()
+ return render(
+ request,
+ 'games/scrape.html',
+ {
+ 'q': keywords,
+ 'form': form,
+ }
+ )
+ else:
+ return HttpResponseBadRequest()
+
+
+@login_required
+def click_to_scrape(request):
+ if request.method == "POST":
+ url = request.POST.get("url")
+ if url:
+ return jump_or_scrape(request, url)
+ else:
+ return HttpResponseBadRequest()
+ else:
+ return HttpResponseBadRequest()
diff --git a/movies/templates/movies/create_update_review.html b/movies/templates/movies/create_update_review.html
index 223d30a0..1f4e5b34 100644
--- a/movies/templates/movies/create_update_review.html
+++ b/movies/templates/movies/create_update_review.html
@@ -49,7 +49,7 @@
{% if movie.year %}({{ movie.year }}){% endif %}
{% endif %}
- {{ movie.get_source_site_display }}
+ {{ movie.get_source_site_display }}
{% if movie.director %}{% trans '导演:' %}
{% for director in movie.director %}
diff --git a/movies/templates/movies/detail.html b/movies/templates/movies/detail.html
index 58146c50..8dbb4a09 100644
--- a/movies/templates/movies/detail.html
+++ b/movies/templates/movies/detail.html
@@ -190,7 +190,7 @@
{% if movie.other_info %}
{% for k, v in movie.other_info.items %}
- {{k}}:{{v}}
+ {{ k }}:{{ v | urlize }}
{% endfor %}
{% endif %}
diff --git a/movies/templates/movies/scrape.html b/movies/templates/movies/scrape.html
index e2cd4730..91cd90c9 100644
--- a/movies/templates/movies/scrape.html
+++ b/movies/templates/movies/scrape.html
@@ -44,7 +44,7 @@
{% trans '根据豆瓣内容填写下方表单' %}
-
+