forked from Mapan/odoo17e
209 lines
8.5 KiB
Python
209 lines
8.5 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
import requests
|
|
|
|
from odoo import _, api, fields, models
|
|
from odoo.exceptions import UserError
|
|
from dateutil.relativedelta import relativedelta
|
|
from werkzeug.urls import url_join
|
|
|
|
|
|
class SocialStreamPostYoutube(models.Model):
|
|
_inherit = 'social.stream.post'
|
|
|
|
youtube_video_id = fields.Char('YouTube Video ID', index=True)
|
|
youtube_likes_count = fields.Integer('YouTube Likes')
|
|
youtube_dislikes_count = fields.Integer('YouTube Dislikes')
|
|
youtube_likes_ratio = fields.Integer('YouTube Likes Ratio', compute='_compute_youtube_likes_ratio')
|
|
youtube_comments_count = fields.Integer('YouTube Comments Count')
|
|
youtube_views_count = fields.Integer('YouTube Views')
|
|
youtube_thumbnail_url = fields.Char('YouTube Thumbnail Url', compute="_compute_youtube_thumbnail_url")
|
|
|
|
def _compute_author_link(self):
|
|
youtube_posts = self._filter_by_media_types(['youtube'])
|
|
super(SocialStreamPostYoutube, (self - youtube_posts))._compute_author_link()
|
|
|
|
for post in youtube_posts:
|
|
post.author_link = 'http://www.youtube.com/channel/%s' % (post.stream_id.account_id.youtube_channel_id)
|
|
|
|
def _compute_post_link(self):
|
|
youtube_posts = self._filter_by_media_types(['youtube'])
|
|
super(SocialStreamPostYoutube, (self - youtube_posts))._compute_post_link()
|
|
|
|
for post in youtube_posts:
|
|
post.post_link = 'https://www.youtube.com/watch?v=%s' % post.youtube_video_id
|
|
|
|
@api.depends('youtube_video_id')
|
|
def _compute_youtube_thumbnail_url(self):
|
|
for post in self:
|
|
post.youtube_thumbnail_url = "http://i3.ytimg.com/vi/%s/hqdefault.jpg" % post.youtube_video_id
|
|
|
|
@api.depends('youtube_likes_count', 'youtube_dislikes_count')
|
|
def _compute_youtube_likes_ratio(self):
|
|
for post in self:
|
|
if not post.youtube_likes_count and not post.youtube_dislikes_count:
|
|
post.youtube_likes_ratio = 0
|
|
else:
|
|
post.youtube_likes_ratio = int(
|
|
100 * post.youtube_likes_count
|
|
/ (post.youtube_likes_count + post.youtube_dislikes_count)
|
|
)
|
|
|
|
def _compute_is_author(self):
|
|
youtube_posts = self._filter_by_media_types(['youtube'])
|
|
super(SocialStreamPostYoutube, (self - youtube_posts))._compute_is_author()
|
|
youtube_posts.is_author = True
|
|
|
|
# ========================================================
|
|
# COMMENTS / LIKES
|
|
# ========================================================
|
|
|
|
def _youtube_comment_add(self, comment_id, message, is_edit=False):
|
|
self.ensure_one()
|
|
self.account_id._refresh_youtube_token()
|
|
|
|
common_params = {
|
|
'access_token': self.account_id.youtube_access_token,
|
|
'part': 'snippet',
|
|
}
|
|
|
|
if comment_id:
|
|
if is_edit:
|
|
# editing own comment
|
|
result_comment = requests.put(
|
|
url_join(self.env['social.media']._YOUTUBE_ENDPOINT, "youtube/v3/comments"),
|
|
params=common_params,
|
|
json={
|
|
'id': comment_id,
|
|
'snippet': {
|
|
'textOriginal': message,
|
|
}
|
|
}
|
|
).json()
|
|
else:
|
|
# reply to comment, uses different endpoint that commenting a video
|
|
result_comment = requests.post(
|
|
url_join(self.env['social.media']._YOUTUBE_ENDPOINT, "youtube/v3/comments"),
|
|
params=common_params,
|
|
json={
|
|
'snippet': {
|
|
'textOriginal': message,
|
|
'parentId': comment_id
|
|
}
|
|
},
|
|
timeout=5
|
|
).json()
|
|
else:
|
|
# brand new comment on the video
|
|
result_comment = requests.post(
|
|
url_join(self.env['social.media']._YOUTUBE_ENDPOINT, "youtube/v3/commentThreads"),
|
|
params=common_params,
|
|
json={
|
|
'snippet': {
|
|
'topLevelComment': {'snippet': {'textOriginal': message}},
|
|
'channelId': self.account_id.youtube_channel_id,
|
|
'videoId': self.youtube_video_id
|
|
},
|
|
},
|
|
timeout=5
|
|
).json().get('snippet', {}).get('topLevelComment')
|
|
|
|
youtube_comment = self.env['social.media']._format_youtube_comment(result_comment)
|
|
youtube_comment.setdefault('comments', {'data': []})
|
|
return youtube_comment
|
|
|
|
def _youtube_comment_delete(self, comment_id):
|
|
self.ensure_one()
|
|
self.account_id._refresh_youtube_token()
|
|
|
|
response = requests.delete(
|
|
url=url_join(self.env['social.media']._YOUTUBE_ENDPOINT, 'youtube/v3/comments'),
|
|
params={
|
|
'id': comment_id,
|
|
'access_token': self.account_id.youtube_access_token,
|
|
}
|
|
)
|
|
|
|
if not response.ok:
|
|
self.account_id._action_disconnect_accounts(response.json())
|
|
|
|
def _youtube_comment_fetch(self, next_page_token=False, count=20):
|
|
self.ensure_one()
|
|
self.stream_id.account_id._refresh_youtube_token()
|
|
|
|
comments_endpoint_url = url_join(self.env['social.media']._YOUTUBE_ENDPOINT, "youtube/v3/commentThreads")
|
|
params = {
|
|
'part': 'snippet,replies',
|
|
'textFormat': 'plainText',
|
|
'access_token': self.stream_id.account_id.youtube_access_token,
|
|
'videoId': self.youtube_video_id,
|
|
'maxResults': count
|
|
}
|
|
|
|
if next_page_token:
|
|
params['pageToken'] = next_page_token
|
|
|
|
result = requests.get(comments_endpoint_url, params=params, timeout=5)
|
|
result_json = result.json()
|
|
|
|
if not result.ok:
|
|
error_message = _('An error occurred.')
|
|
|
|
if result_json.get('error'):
|
|
error_code = result_json['error'].get('code')
|
|
error_reason = result_json['error'].get('errors', [{}])[0].get('reason')
|
|
if error_code == 404 and error_reason == 'videoNotFound':
|
|
error_message = _("Video not found. It could have been removed from Youtube.")
|
|
elif error_code == 403 and error_reason == 'commentsDisabled':
|
|
error_message = _("Comments are marked as 'disabled' for this video. It could have been set as 'private'.")
|
|
|
|
raise UserError(error_message)
|
|
|
|
comments = []
|
|
for comment in result_json.get('items', []):
|
|
youtube_comment = self.env['social.media']._format_youtube_comment(
|
|
comment.get('snippet').get('topLevelComment'))
|
|
|
|
youtube_comment_replies = [
|
|
self.env['social.media']._format_youtube_comment(reply)
|
|
for reply in list(reversed(comment.get('replies', {}).get('comments', [])))]
|
|
|
|
youtube_comment['comments'] = {
|
|
'data': youtube_comment_replies if youtube_comment_replies else []
|
|
}
|
|
|
|
comments.append(youtube_comment)
|
|
|
|
return {
|
|
'comments': comments,
|
|
'nextPageToken': result_json.get('nextPageToken')
|
|
}
|
|
|
|
# ========================================================
|
|
# MISC / UTILITY
|
|
# ========================================================
|
|
|
|
def _fetch_matching_post(self):
|
|
self.ensure_one()
|
|
|
|
if self.account_id.media_type == 'youtube' and self.youtube_video_id:
|
|
return self.env['social.live.post'].search(
|
|
[('youtube_video_id', '=', self.youtube_video_id)], limit=1
|
|
).post_id
|
|
else:
|
|
return super(SocialStreamPostYoutube, self)._fetch_matching_post()
|
|
|
|
@api.autovacuum
|
|
def _gc_youtube_data(self):
|
|
""" According to Youtube API terms of service, users Youtube data have to be removed
|
|
if they have not been updated for more than 30 days.
|
|
Ref: https://developers.google.com/youtube/terms/developer-policies#e.-handling-youtube-data-and-content
|
|
(Section 4. "Refreshing, Storing, and Displaying API Data") """
|
|
|
|
youtube_stream = self.env.ref('social_youtube.stream_type_youtube_channel_videos')
|
|
self.env['social.stream.post'].sudo().search([
|
|
('stream_id.stream_type_id', '=', youtube_stream.id),
|
|
('write_date', '<', fields.Datetime.now() - relativedelta(days=30))
|
|
]).unlink()
|