Skip to content

Commit

Permalink
Implement dislikes (#20)
Browse files Browse the repository at this point in the history
* Youtube likes

* Reddit downvotes
  • Loading branch information
amadejkastelic authored Oct 17, 2024
1 parent 01ed381 commit 5b86b29
Show file tree
Hide file tree
Showing 9 changed files with 79 additions and 9 deletions.
8 changes: 5 additions & 3 deletions bot/domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
🧑🏻‍🎨 Author: {author}
📅 Created: {created}
👀 Views: {views}
👍🏻 Likes: {likes}
👍🏻 Likes: {likes} 👎🏻 Dislikes: {dislikes}
📕 Description: {description}\n
"""

Expand Down Expand Up @@ -95,6 +95,7 @@ class Post:
description: typing.Optional[str] = None
views: typing.Optional[int] = None
likes: typing.Optional[int] = None
dislikes: typing.Optional[int] = None
buffer: typing.Optional[io.BytesIO] = None
spoiler: bool = False
created: typing.Optional[datetime.datetime] = None
Expand All @@ -109,8 +110,9 @@ def __str__(self) -> str:
author=self.author or '❌',
created=utils.date_to_human_format(self.created) if self.created else '❌',
description=description if not self.spoiler else f'||{description}||',
views=utils.number_to_human_format(self.views) if self.views else '❌',
likes=utils.number_to_human_format(self.likes) if self.likes else '❌',
views=utils.number_to_human_format(self.views) if self.views is not None else '❌',
likes=utils.number_to_human_format(self.likes) if self.likes is not None else '❌',
dislikes=utils.number_to_human_format(self.dislikes) if self.dislikes is not None else '❌',
)

def set_format(self, fmt: typing.Optional[str]) -> None:
Expand Down
10 changes: 8 additions & 2 deletions bot/integrations/reddit/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import uuid

import asyncpraw
import asyncpraw.models
import redvid
import requests
from RedDownloader import RedDownloader as reddit_downloader
Expand Down Expand Up @@ -107,7 +108,7 @@ async def _hydrate_post(self, post: domain.Post) -> bool:
return self._hydrate_post_no_login(post)

try:
submission = await self.client.submission(url=post.url)
submission: asyncpraw.models.Submission = await self.client.submission(url=post.url)
except praw_exceptions.InvalidURL:
# Hack for new reddit urls generated in mobile app
# Does another request, which redirects to the correct url
Expand All @@ -120,7 +121,7 @@ async def _hydrate_post(self, post: domain.Post) -> bool:

post.author = submission.author
post.description = f'{submission.title}{content}'
post.likes = submission.score
post.likes, post.dislikes = self._calculate_votes(upvotes=submission.score, ratio=submission.upvote_ratio)
post.spoiler = submission.over_18 or submission.spoiler
post.created = datetime.datetime.fromtimestamp(submission.created_utc).astimezone()

Expand Down Expand Up @@ -196,3 +197,8 @@ def _download_and_merge_gallery(self, url: str) -> typing.Optional[io.BytesIO]:
def _is_nsfw(url: str) -> bool:
content = str(requests.get(url, timeout=base.DEFAULT_TIMEOUT).content)
return 'nsfw":true' in content or 'isNsfw":true' in content

@staticmethod
def _calculate_votes(upvotes: int, ratio: float) -> typing.Tuple[int, int]:
downvotes = (upvotes / ratio) - upvotes
return (upvotes, int(downvotes))
19 changes: 18 additions & 1 deletion bot/integrations/youtube/client.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import io
import typing

import aiohttp
import pytubefix as pytube
from django.conf import settings

Expand All @@ -10,6 +11,9 @@
from bot import logger
from bot.integrations import base
from bot.integrations.youtube import config
from bot.integrations.youtube import types

LIKES_API_URL_TEMPLATE = 'https://returnyoutubedislikeapi.com/votes?videoId={video_id}'


class YoutubeClientSingleton(base.BaseClientSingleton):
Expand All @@ -25,12 +29,15 @@ def _create_instance(cls) -> None:
cls._INSTANCE = base.MISSING
return

cls._INSTANCE = YoutubeClient()
cls._INSTANCE = YoutubeClient(fetch_likes=conf.external_likes_api)


class YoutubeClient(base.BaseClient):
INTEGRATION = constants.Integration.YOUTUBE

def __init__(self, fetch_likes: bool) -> None:
self.fetch_likes = fetch_likes

async def get_integration_data(self, url: str) -> typing.Tuple[constants.Integration, str, typing.Optional[int]]:
return self.INTEGRATION, url.strip('/').split('?')[0].split('/')[-1], None

Expand All @@ -47,6 +54,11 @@ async def get_post(self, url: str) -> domain.Post:
spoiler=vid.age_restricted is True,
)

if self.fetch_likes:
likes = await self._get_likes(video_id=vid.video_id)
post.likes = likes.likes
post.dislikes = likes.dislikes

vid.streams.filter(progressive=True, file_extension='mp4').order_by(
'resolution'
).desc().first().stream_to_buffer(post.buffer)
Expand All @@ -55,3 +67,8 @@ async def get_post(self, url: str) -> domain.Post:

async def get_comments(self, url: str, n: int = 5) -> typing.List[domain.Comment]:
raise exceptions.NotSupportedError('get_comments')

async def _get_likes(self, video_id: str) -> types.Likes:
async with aiohttp.ClientSession() as session:
async with session.get(url=LIKES_API_URL_TEMPLATE.format(video_id=video_id)) as resp:
return types.Likes.model_validate(await resp.json())
4 changes: 1 addition & 3 deletions bot/integrations/youtube/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,4 @@


class YoutubeConfig(base.BaseClientConfig):
"""
No additional settings for Youtube integration
"""
external_likes_api: bool = False
22 changes: 22 additions & 0 deletions bot/integrations/youtube/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import datetime

import pydantic
import pydantic.alias_generators


class Likes(pydantic.BaseModel):
model_config = pydantic.ConfigDict(
alias_generator=pydantic.alias_generators.to_camel,
populate_by_name=True,
from_attributes=True,
)

id: str
date_created: datetime.datetime
likes: int
raw_dislikes: int
raw_likes: int
dislikes: int
rating: int
view_count: int
deleted: bool
18 changes: 18 additions & 0 deletions bot/migrations/0002_post_dislikes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.1.2 on 2024-10-17 07:36

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('bot', '0001_initial'),
]

operations = [
migrations.AddField(
model_name='post',
name='dislikes',
field=models.BigIntegerField(default=None, null=True),
),
]
4 changes: 4 additions & 0 deletions bot/models/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ class Post(models.Model):
null=True,
default=None,
)
dislikes = models.BigIntegerField(
null=True,
default=None,
)
spoiler = models.BooleanField(
null=False,
default=False,
Expand Down
2 changes: 2 additions & 0 deletions bot/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ def get_post(
description=post.description,
views=post.views,
likes=post.likes,
dislikes=post.dislikes,
buffer=io.BytesIO(post.blob) if post.blob else None,
spoiler=post.spoiler,
created=post.posted_at,
Expand All @@ -223,6 +224,7 @@ def save_post(
description=post.description,
views=post.views,
likes=post.likes,
dislikes=post.dislikes,
spoiler=post.spoiler,
posted_at=post.created,
blob=post.read_buffer(),
Expand Down
1 change: 1 addition & 0 deletions conf/settings_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@
},
'youtube': {
'enabled': False,
'external_likes_api': False,
},
'threads': {
'enable': False,
Expand Down

0 comments on commit 5b86b29

Please sign in to comment.