|
import gradio as gr |
|
import os |
|
import json |
|
import pandas as pd |
|
from googleapiclient.discovery import build |
|
|
|
|
|
class YouTube: |
|
def __init__(self, api_key, channel_id): |
|
self.api_key = api_key |
|
self.channel_id = channel_id |
|
self.youtube = build("youtube", "v3", developerKey=api_key) |
|
|
|
|
|
def get_general_channel_info(self): |
|
try: |
|
request = self.youtube.channels().list(part="snippet,statistics", id=self.channel_id) |
|
response = request.execute() |
|
channel_info = response["items"][0] |
|
|
|
channel_stats = { |
|
"Subscribers": channel_info["statistics"]["subscriberCount"], |
|
"Views": channel_info["statistics"]["viewCount"], |
|
"Total videos": channel_info["statistics"]["videoCount"], |
|
"Title": channel_info["snippet"]["title"], |
|
"Description": channel_info["snippet"]["description"], |
|
"Cover": channel_info["snippet"]["thumbnails"]["high"]["url"], |
|
"URL": f"https://www.youtube.com/channel/{self.channel_id}", |
|
} |
|
return channel_stats |
|
except Exception as e: |
|
return {"error": str(e)} |
|
|
|
|
|
def get_video_data(self): |
|
try: |
|
channel_response = self.youtube.channels().list(part="contentDetails", id=self.channel_id).execute() |
|
playlist_id = channel_response["items"][0]["contentDetails"]["relatedPlaylists"]["uploads"] |
|
|
|
videos = [] |
|
next_page_token = None |
|
|
|
while True: |
|
playlist_items_response = self.youtube.playlistItems().list( |
|
part="snippet", |
|
maxResults=50, |
|
playlistId=playlist_id, |
|
pageToken=next_page_token, |
|
).execute() |
|
videos.extend(playlist_items_response["items"]) |
|
next_page_token = playlist_items_response.get("nextPageToken") |
|
if next_page_token is None: |
|
break |
|
|
|
video_data = [] |
|
for video in videos: |
|
video_id = video["snippet"]["resourceId"]["videoId"] |
|
video_details = self.youtube.videos().list(part="statistics,snippet", id=video_id).execute() |
|
|
|
if video_details["items"]: |
|
video_info = video_details["items"][0] |
|
snippet = video_info["snippet"] |
|
statistics = video_info["statistics"] |
|
|
|
video_data.append( |
|
{ |
|
"Video": f"https://www.youtube.com/watch?v={video_id}", |
|
"Thumbnail": snippet["thumbnails"]["high"]["url"], |
|
"Title": snippet["title"], |
|
"PublishedAt": snippet["publishedAt"], |
|
"π": statistics.get("viewCount", 0), |
|
"π": statistics.get("likeCount", 0), |
|
"π¬": statistics.get("commentCount", 0) |
|
} |
|
) |
|
|
|
df = pd.DataFrame(video_data) |
|
return df |
|
except Exception as e: |
|
return {"error": str(e)} |
|
|
|
|
|
def load_scheme(file_path): |
|
with open(file_path) as f: |
|
return json.load(f) |
|
|
|
|
|
def display_channel_statistics(statistics): |
|
if "error" in statistics: |
|
return statistics["error"] |
|
return f""" |
|
<div style='text-align: center;'> |
|
<img src='{statistics["Cover"]}' style='width: 100%; max-width: 300px; height: auto; margin-bottom: 15px;'><br> |
|
<a href='{statistics["URL"]}' style='font-size: 1.5em; font-weight: bold; color: #2c3e50;'>{statistics["Title"]}</a><br> |
|
<div style='font-size: 1.1em; color: #7f8c8d;'> |
|
Subscribers: {statistics["Subscribers"]}<br> |
|
Views: {statistics["Views"]}<br> |
|
Total videos: {statistics["Total videos"]} |
|
</div> |
|
</div> |
|
""" |
|
|
|
|
|
def display_video_data(video_data): |
|
if "error" in video_data: |
|
return video_data["error"] |
|
video_data['Title'] = video_data['Title'].apply(lambda x: x.split('#')[0]) |
|
video_data['PublishedAt'] = pd.to_datetime(video_data['PublishedAt']).dt.date |
|
video_data['Video'] = video_data.apply( |
|
lambda row: f'<a href="{row["Video"]}" target="_blank">{row["Title"]}</a><br>{row["PublishedAt"]}', axis=1 |
|
) |
|
video_data['Thumbnail'] = video_data['Thumbnail'].apply(lambda x: f'<img src="{x}" style="width: 100%; max-width: 100px; height: auto; margin: 5px 0;"><br>') |
|
video_data['Video'] = video_data['Thumbnail'] + video_data['Video'] |
|
video_data = video_data.drop(['Thumbnail', 'Title', 'PublishedAt'], axis=1) |
|
|
|
|
|
custom_css = """ |
|
<style> |
|
table { |
|
width: 100%; |
|
border-collapse: collapse; |
|
margin: 25px 0; |
|
font-size: 1em; |
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
|
box-shadow: 0 0 20px rgba(0, 0, 0, 0.05); |
|
border-radius: 10px; |
|
overflow: hidden; |
|
} |
|
th, td { |
|
padding: 15px; |
|
background-color: #ffffff; |
|
color: #333333; |
|
} |
|
th { |
|
background-color: #f8f9fa; |
|
font-weight: bold; |
|
text-align: center; |
|
border-bottom: 2px solid #e9ecef; |
|
} |
|
tr { |
|
border-bottom: 1px solid #e9ecef; |
|
} |
|
tr:nth-of-type(even) { |
|
background-color: #f8f9fa; |
|
} |
|
tr:last-of-type { |
|
border-bottom: none; |
|
} |
|
tr:hover { |
|
background-color: #f1f1f1; |
|
} |
|
</style> |
|
""" |
|
|
|
return custom_css + video_data.to_html(escape=False, index=False).replace('text-align: right;', 'text-align: center;') |
|
|
|
|
|
api_key = os.getenv('API_KEY') |
|
scheme = load_scheme("scheme.json") |
|
|
|
|
|
def fetch_data(selected_category, platform): |
|
channel_id = scheme[selected_category][platform] |
|
if platform == "youtube": |
|
youtube_account = YouTube(api_key, channel_id) |
|
|
|
statistics = youtube_account.get_general_channel_info() |
|
statistics_display = display_channel_statistics(statistics) |
|
|
|
video_data = youtube_account.get_video_data() |
|
video_data_display = display_video_data(video_data) |
|
|
|
return statistics_display, video_data_display |
|
|
|
|
|
css = """ |
|
<style> |
|
@media (max-width: 600px) { |
|
.fixed-top { |
|
position: fixed; |
|
top: 0; |
|
width: 100%; |
|
z-index: 999; |
|
background: white; |
|
padding: 10px 0; |
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
|
} |
|
body { |
|
padding-top: 70px; /* Adjust based on the height of the fixed element */ |
|
} |
|
} |
|
body { |
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
|
background-color: #f3f4f6; |
|
color: #333333; |
|
} |
|
.gradio-container { |
|
padding: 20px; |
|
} |
|
.gr-inputs { |
|
display: flex; |
|
justify-content: center; |
|
margin-bottom: 20px; |
|
} |
|
.gr-inputs > div { |
|
margin: 0 10px; |
|
} |
|
.gr-output { |
|
background: white; |
|
border-radius: 10px; |
|
padding: 20px; |
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); |
|
} |
|
</style> |
|
""" |
|
|
|
gr_interface = gr.Interface( |
|
fn=fetch_data, |
|
inputs=[ |
|
gr.Dropdown(choices=list(scheme.keys()), label="Select Channel", elem_classes=["fixed-top"]), |
|
gr.Dropdown(choices=list(next(iter(scheme.values())).keys()), label="Select Platform", elem_classes=["fixed-top"]) |
|
], |
|
outputs=[ |
|
gr.HTML(label="General Channel Statistics"), |
|
gr.HTML(label="Video Data") |
|
], |
|
title="Social Media Analytics", |
|
description="Select a channel and platform to fetch analytics data.", |
|
css=css |
|
) |
|
|
|
gr_interface.launch() |
|
|