File size: 6,804 Bytes
64db1cd
 
 
 
 
 
 
 
 
 
bdbc2da
b6549ee
 
26c78a4
64db1cd
 
b6549ee
af1173c
64db1cd
 
 
b6549ee
 
64db1cd
 
 
 
 
 
 
 
 
 
 
 
 
 
6ce99f0
 
 
 
 
 
 
64db1cd
 
b6549ee
 
64db1cd
 
 
 
 
 
 
ace5c70
64db1cd
 
 
 
 
b6549ee
 
64db1cd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b6549ee
6ce99f0
b6549ee
6ce99f0
64db1cd
 
 
 
 
 
 
 
6ce99f0
64db1cd
 
 
6ce99f0
64db1cd
 
 
6ce99f0
 
 
64db1cd
 
 
 
 
6ce99f0
64db1cd
 
 
 
 
 
6ce99f0
64db1cd
 
 
6ce99f0
64db1cd
 
 
 
6ce99f0
64db1cd
 
 
12216f2
f5fe94e
 
64db1cd
 
b6549ee
64db1cd
6ce99f0
64db1cd
 
 
 
 
 
 
 
 
 
 
b6549ee
64db1cd
 
 
 
 
 
 
 
 
 
 
 
 
ace5c70
64db1cd
 
ace5c70
64db1cd
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
import gradio as gr
import pandas as pd
from googleapiclient.discovery import build
import plotly.express as px
import base64
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.cluster import KMeans
from datetime import datetime, timedelta
import os
from huggingface_hub import InferenceClient  # Hugging Face Hub API μ‚¬μš©

# 여기에 YouTube API ν‚€λ₯Ό μž…λ ₯ν•˜μ„Έμš”
YOUTUBE_API_KEY = "AIzaSyA9DEIHCYexeF2gSFW8cF6E3JTu9BhYxLc"

def create_client(model_name):
    token = os.getenv("HF_TOKEN")
    return InferenceClient(model=model_name, token=token)

client = create_client("CohereForAI/c4ai-command-r-plus")

def get_video_stats(video_id):
    youtube = build("youtube", "v3", developerKey=YOUTUBE_API_KEY)
    video_response = youtube.videos().list(
        part="snippet,statistics",
        id=video_id
    ).execute()

    video = video_response["items"][0]
    title = video["snippet"]["title"]
    channel_id = video["snippet"]["channelId"]
    publish_time = video["snippet"]["publishedAt"]
    view_count = int(video["statistics"].get("viewCount", 0))
    like_count = int(video["statistics"].get("likeCount", 0))
    comment_count = int(video["statistics"].get("commentCount", 0))

    return {
        "λ™μ˜μƒ ID": video_id,
        "제λͺ©": title,
        "κ²Œμ‹œ μ‹œκ°„": publish_time,
        "채널 ID": channel_id,
        "쑰회수": view_count,
        "μ’‹μ•„μš” 수": like_count,
        "λŒ“κΈ€ 수": comment_count
    }

def get_channel_stats(channel_id):
    youtube = build("youtube", "v3", developerKey=YOUTUBE_API_KEY)
    channel_response = youtube.channels().list(
        part="statistics",
        id=channel_id
    ).execute()

    if channel_response["items"]:
        channel = channel_response["items"][0]
        subscriber_count = int(channel["statistics"]["subscriberCount"])
    else:
        subscriber_count = 0

    return subscriber_count

def get_video_data(query, max_results, published_after, published_before):
    youtube = build("youtube", "v3", developerKey=YOUTUBE_API_KEY)
    video_ids = []
    next_page_token = None

    while len(video_ids) < max_results:
        search_response = youtube.search().list(
            q=query,
            type="video",
            part="id",
            maxResults=50,
            pageToken=next_page_token,
            order="viewCount",
            publishedAfter=published_after,
            publishedBefore=published_before
        ).execute()

        video_ids.extend([item["id"]["videoId"] for item in search_response["items"]])
        next_page_token = search_response.get("nextPageToken")

        if not next_page_token:
            break

    video_ids = video_ids[:max_results]

    video_stats = []
    for video_id in video_ids:
        stats = get_video_stats(video_id)
        channel_id = stats["채널 ID"]
        subscriber_count = get_channel_stats(channel_id)
        stats["κ΅¬λ…μž 수"] = subscriber_count
        video_stats.append(stats)

    video_stats_df = pd.DataFrame(video_stats)
    return video_stats_df

def download_csv(df, filename):
    csv = df.to_csv(index=False)
    b64 = base64.b64encode(csv.encode()).decode()
    href = f'<a href="data:file/csv;base64,{b64}" download="{filename}.csv">λ‹€μš΄λ‘œλ“œ {filename} CSV</a>'
    return href

def visualize_video_ranking(video_stats_df):
    video_stats_df["ν™œμ„± μ§€μˆ˜"] = video_stats_df["쑰회수"] / video_stats_df["κ΅¬λ…μž 수"]

    csv_download_link = download_csv(video_stats_df, "video_stats")

    fig = px.bar(video_stats_df, x="λ™μ˜μƒ ID", y="ν™œμ„± μ§€μˆ˜", color="쑰회수",
                 labels={"λ™μ˜μƒ ID": "λ™μ˜μƒ ID", "ν™œμ„± μ§€μˆ˜": "ν™œμ„± μ§€μˆ˜"},
                 title="λ™μ˜μƒ ν™œμ„± μ§€μˆ˜")
    fig.update_layout(height=500, width=500)

    return video_stats_df, fig, csv_download_link

def analyze_titles(video_stats_df, n_clusters=5):
    titles = video_stats_df['제λͺ©'].tolist()
    vectorizer = TfidfVectorizer()
    tfidf_matrix = vectorizer.fit_transform(titles)

    kmeans = KMeans(n_clusters=n_clusters, random_state=42)
    kmeans.fit(tfidf_matrix)
    labels = kmeans.labels_
    video_stats_df["ν΄λŸ¬μŠ€ν„°"] = labels

    cluster_summaries = []
    for i in range(n_clusters):
        cluster_titles = video_stats_df[video_stats_df["ν΄λŸ¬μŠ€ν„°"] == i]['제λͺ©'].tolist()
        cluster_text = ' '.join(cluster_titles)
        summary = summarize_cluster(cluster_text, i)
        cluster_summaries.append(summary)

    cluster_summary_df = pd.DataFrame({'ν΄λŸ¬μŠ€ν„°': range(n_clusters), 'μš”μ•½': cluster_summaries})
    return cluster_summary_df

def summarize_cluster(cluster_text, cluster_num):
    prompt = f"λ‹€μŒ λ™μ˜μƒμ„ λΆ„μ„ν•˜μ—¬ μš”μ•½ν•˜κ³ , 500자 μ΄λ‚΄λ‘œ λ™μ˜μƒμ˜ νŠΉμ§• 및 인기 μš”μΈμ„ μ„€λͺ…ν•΄μ£Όμ„Έμš”: {cluster_text}"
    response = client.generate(inputs=prompt)
    summary = response.generated_text.strip()
    return summary

def main(query, max_results, period, page, n_clusters=5):
    if query:
        # κΈ°κ°„ μ„€μ •
        now = datetime.utcnow()
        published_before = now.isoformat("T") + "Z"
        if period == "1주일":
            published_after = (now - timedelta(days=7)).isoformat("T") + "Z"
        elif period == "1κ°œμ›”":
            published_after = (now - timedelta(days=30)).isoformat("T") + "Z"
        elif period == "3κ°œμ›”":
            published_after = (now - timedelta(days=90)).isoformat("T") + "Z"
        else:
            published_after = (now - timedelta(days=30)).isoformat("T") + "Z"  # κΈ°λ³Έκ°’ 1κ°œμ›”

        video_stats_df = get_video_data(query, max_results, published_after, published_before)

        if page == "Video Ranking":
            video_stats_df, fig, csv_download_link = visualize_video_ranking(video_stats_df)
            return video_stats_df, fig, csv_download_link

        elif page == "Title Analysis":
            cluster_summary_df = analyze_titles(video_stats_df, n_clusters)
            return cluster_summary_df, None, None

iface = gr.Interface(
    fn=main,
    inputs=[
        gr.components.Textbox(label="검색 쿼리"),
        gr.components.Number(label="μ΅œλŒ€ κ²°κ³Ό 수", value=5, precision=0, minimum=1, maximum=1000),
        gr.components.Dropdown(["1주일", "1κ°œμ›”", "3κ°œμ›”"], label="κΈ°κ°„"),
        gr.components.Dropdown(["Video Ranking", "Title Analysis"], label="νŽ˜μ΄μ§€"),
        gr.components.Number(label="ν΄λŸ¬μŠ€ν„° 수", value=5, precision=0, minimum=2, maximum=10)
    ],
    outputs=[
        gr.components.Dataframe(label="κ²°κ³Ό"),
        gr.components.Plot(label="κ·Έλž˜ν”„"),
        gr.components.HTML(label="CSV λ‹€μš΄λ‘œλ“œ 링크")
    ],
    live=False,
    title="YouTube 뢄석 도ꡬ"
)

if __name__ == "__main__":
    iface.launch()