增加通过用户uniqueId获取播放量前十的视频功能

This commit is contained in:
wanjia 2025-03-05 17:59:31 +08:00
parent 2713cba136
commit 4ccfc764f7
31 changed files with 1797 additions and 1313 deletions

View File

@ -1,179 +1,182 @@
""" """
Django settings for automated_task_monitor project. Django settings for automated_task_monitor project.
Generated by 'django-admin startproject' using Django 5.1.6. Generated by 'django-admin startproject' using Django 5.1.6.
For more information on this file, see For more information on this file, see
https://docs.djangoproject.com/en/5.1/topics/settings/ https://docs.djangoproject.com/en/5.1/topics/settings/
For the full list of settings and their values, see For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.1/ref/settings/ https://docs.djangoproject.com/en/5.1/ref/settings/
""" """
from pathlib import Path from pathlib import Path
import os import os
import time import time
# Build paths inside the project like this: BASE_DIR / 'subdir'. # Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production # Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret! # SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-!1v8ca8ows01-*m5(&9)qgk5jc-my^q+4d+0)s_*^n&vne^&w9' SECRET_KEY = 'django-insecure-!1v8ca8ows01-*m5(&9)qgk5jc-my^q+4d+0)s_*^n&vne^&w9'
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True DEBUG = True
ALLOWED_HOSTS = [ ALLOWED_HOSTS = [
'127.0.0.1', # 本地开发 '127.0.0.1', # 本地开发
'localhost', # 本地开发 'localhost', # 本地开发
'81.69.223.133', # 你的服务器IP '81.69.223.133', # 你的服务器IP
] ]
# 监控配置 # 监控配置
MONITOR_INTERVAL = 5 # 缩短为5秒 MONITOR_INTERVAL = 5 # 缩短为5秒
# 日志写入间隔(秒) # 日志写入间隔(秒)
LOG_INTERVAL = 60 # 每60秒写入一次日志 LOG_INTERVAL = 60 # 每60秒写入一次日志
# Application definition # Application definition
INSTALLED_APPS = [ INSTALLED_APPS = [
'django.contrib.admin', 'django.contrib.admin',
'django.contrib.auth', 'django.contrib.auth',
'django.contrib.contenttypes', 'django.contrib.contenttypes',
'django.contrib.sessions', 'django.contrib.sessions',
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'monitor.apps.MonitorConfig', 'monitor.apps.MonitorConfig',
] ]
MIDDLEWARE = [ MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware', 'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
] ]
ROOT_URLCONF = 'automated_task_monitor.urls' ROOT_URLCONF = 'automated_task_monitor.urls'
TEMPLATES = [ TEMPLATES = [
{ {
'BACKEND': 'django.template.backends.django.DjangoTemplates', 'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'] 'DIRS': [BASE_DIR / 'templates']
, ,
'APP_DIRS': True, 'APP_DIRS': True,
'OPTIONS': { 'OPTIONS': {
'context_processors': [ 'context_processors': [
'django.template.context_processors.debug', 'django.template.context_processors.debug',
'django.template.context_processors.request', 'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth', 'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages', 'django.contrib.messages.context_processors.messages',
], ],
}, },
}, },
] ]
WSGI_APPLICATION = 'automated_task_monitor.wsgi.application' WSGI_APPLICATION = 'automated_task_monitor.wsgi.application'
# Database # Database
# https://docs.djangoproject.com/en/5.1/ref/settings/#databases # https://docs.djangoproject.com/en/5.1/ref/settings/#databases
DATABASES = { DATABASES = {
'default': { 'default': {
'ENGINE': 'django.db.backends.mysql', 'ENGINE': 'django.db.backends.mysql',
'NAME': 'monitoring_db', 'NAME': 'monitoring_db',
'USER': 'root', 'USER': 'root',
'PASSWORD': '123456', 'PASSWORD': '123456',
'HOST': 'localhost', 'HOST': 'localhost',
'PORT': '3306', 'PORT': '3306',
} 'OPTIONS': {
} 'charset': 'utf8mb4',
},
}
# Password validation }
# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [ # Password validation
{ # https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
}, AUTH_PASSWORD_VALIDATORS = [
{ {
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
}, },
{ {
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
}, },
{ {
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
}, },
] {
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
# Internationalization ]
# https://docs.djangoproject.com/en/5.1/topics/i18n/
LANGUAGE_CODE = 'en-us' # Internationalization
# https://docs.djangoproject.com/en/5.1/topics/i18n/
TIME_ZONE = 'Asia/Shanghai'
LANGUAGE_CODE = 'en-us'
USE_I18N = True
TIME_ZONE = 'Asia/Shanghai'
USE_TZ = True
USE_I18N = True
# Static files (CSS, JavaScript, Images) USE_TZ = True
# https://docs.djangoproject.com/en/5.1/howto/static-files/
STATIC_URL = '/static/' # Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.1/howto/static-files/
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static'), STATIC_URL = '/static/'
]
STATICFILES_DIRS = [
# 如果是生产环境,还需要设置 os.path.join(BASE_DIR, 'static'),
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') ]
# Default primary key field type # 如果是生产环境,还需要设置
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' # Default primary key field type
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
# 日志配置
LOGGING = { DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
'version': 1,
'disable_existing_loggers': False, # 日志配置
'handlers': { LOGGING = {
'console': { 'version': 1,
'class': 'logging.StreamHandler', 'disable_existing_loggers': False,
'formatter': 'simple', 'handlers': {
}, 'console': {
}, 'class': 'logging.StreamHandler',
'formatters': { 'formatter': 'simple',
'simple': { },
'format': '{asctime} {message}', },
'style': '{', 'formatters': {
'datefmt': '%Y-%m-%d %H:%M:%S', 'simple': {
}, 'format': '{asctime} {message}',
}, 'style': '{',
'loggers': { 'datefmt': '%Y-%m-%d %H:%M:%S',
'django.server': { },
'handlers': ['console'], },
'level': 'INFO', 'loggers': {
}, 'django.server': {
}, 'handlers': ['console'],
} 'level': 'INFO',
},
# 确保日志目录存在 },
os.makedirs('logs', exist_ok=True) }
# 添加版本号,防止缓存 # 确保日志目录存在
STATIC_VERSION = int(time.time()) os.makedirs('logs', exist_ok=True)
# 添加版本号,防止缓存
STATIC_VERSION = int(time.time())

View File

@ -1,27 +1,28 @@
""" """
URL configuration for automated_task_monitor project. URL configuration for automated_task_monitor project.
The `urlpatterns` list routes URLs to views. For more information please see: The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/5.1/topics/http/urls/ https://docs.djangoproject.com/en/5.1/topics/http/urls/
Examples: Examples:
Function views Function views
1. Add an import: from my_app import views 1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home') 2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views Class-based views
1. Add an import: from other_app.views import Home 1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf Including another URLconf
1. Import the include() function: from django.urls import include, path 1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
""" """
from django.contrib import admin from django.contrib import admin
from django.urls import path, include from django.urls import path, include
from django.conf import settings from django.conf import settings
from django.conf.urls.static import static from django.conf.urls.static import static
from django.shortcuts import redirect from django.shortcuts import redirect
urlpatterns = [ urlpatterns = [
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
path('monitor/', include('monitor.urls')), path('monitor/', include('monitor.urls')),
path('', lambda request: redirect('monitor/')), # 将根路径重定向到 monitor/ path('', lambda request: redirect('monitor/')), # 将根路径重定向到 monitor/
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) path('', include('monitor.urls')), # 假设您的应用名为 'monitor'
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,33 @@
# Generated by Django 5.1.6 on 2025-03-05 05:35
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('monitor', '0003_allresourceprocess'),
]
operations = [
migrations.CreateModel(
name='DouyinUserVideos',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('sec_user_id', models.CharField(max_length=100, unique=True, verbose_name='用户ID')),
('nickname', models.CharField(max_length=100, verbose_name='昵称')),
('signature', models.TextField(blank=True, verbose_name='签名')),
('follower_count', models.IntegerField(default=0, verbose_name='粉丝数')),
('total_favorited', models.IntegerField(default=0, verbose_name='获赞数')),
('avatar_url', models.CharField(blank=True, max_length=255, verbose_name='头像URL')),
('video_paths', models.TextField(blank=True, verbose_name='视频路径JSON')),
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
],
options={
'verbose_name': '抖音用户视频',
'verbose_name_plural': '抖音用户视频',
'db_table': 'douyin_user_videos',
},
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 5.1.6 on 2025-03-05 05:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('monitor', '0004_douyinuservideos'),
]
operations = [
migrations.AddField(
model_name='douyinuservideos',
name='videos_folder',
field=models.CharField(blank=True, max_length=255, verbose_name='视频文件夹路径'),
),
migrations.AlterField(
model_name='douyinuservideos',
name='video_paths',
field=models.TextField(blank=True, verbose_name='视频信息JSON'),
),
]

View File

@ -0,0 +1,36 @@
# Generated by Django 5.1.6 on 2025-03-05 06:41
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('monitor', '0005_douyinuservideos_videos_folder_and_more'),
]
operations = [
migrations.CreateModel(
name='TiktokUserVideos',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('sec_user_id', models.CharField(max_length=255, unique=True)),
('nickname', models.CharField(max_length=255)),
('signature', models.TextField(blank=True, null=True)),
('follower_count', models.IntegerField(default=0)),
('total_favorited', models.IntegerField(default=0)),
('avatar_url', models.URLField(blank=True, null=True)),
('videos_folder', models.CharField(max_length=255)),
('video_paths', models.TextField(blank=True, null=True)),
('create_time', models.DateTimeField(auto_now_add=True)),
('update_time', models.DateTimeField(auto_now=True)),
],
options={
'verbose_name': 'TikTok用户视频',
'verbose_name_plural': 'TikTok用户视频',
},
),
migrations.DeleteModel(
name='DouyinUserVideos',
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 5.1.6 on 2025-03-05 06:59
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('monitor', '0006_tiktokuservideos_delete_douyinuservideos'),
]
operations = [
migrations.AlterField(
model_name='tiktokuservideos',
name='avatar_url',
field=models.TextField(blank=True, null=True),
),
]

View File

@ -1,75 +1,96 @@
from django.db import models from django.db import models
from django.utils import timezone from django.utils import timezone
class BaseProcessMonitor(models.Model): class BaseProcessMonitor(models.Model):
"""基础进程监控模型""" """基础进程监控模型"""
pid = models.IntegerField(verbose_name='进程ID') pid = models.IntegerField(verbose_name='进程ID')
process_name = models.CharField(max_length=255, verbose_name='进程名称') process_name = models.CharField(max_length=255, verbose_name='进程名称')
is_active = models.BooleanField(default=True, verbose_name='是否活跃') is_active = models.BooleanField(default=True, verbose_name='是否活跃')
status = models.IntegerField(default=1, verbose_name='进程状态') # 1: 运行中, 0: 已终止 status = models.IntegerField(default=1, verbose_name='进程状态') # 1: 运行中, 0: 已终止
created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间') created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间') updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间')
log_file = models.CharField(max_length=255, null=True, blank=True, verbose_name='日志文件路径') log_file = models.CharField(max_length=255, null=True, blank=True, verbose_name='日志文件路径')
class Meta: class Meta:
abstract = True abstract = True
class HighCPUProcess(BaseProcessMonitor): class HighCPUProcess(BaseProcessMonitor):
"""CPU高占用进程监控""" """CPU高占用进程监控"""
cpu_usage = models.FloatField(default=0, verbose_name='CPU使用率') cpu_usage = models.FloatField(default=0, verbose_name='CPU使用率')
cpu_cores = models.IntegerField(default=0, verbose_name='CPU核心数') cpu_cores = models.IntegerField(default=0, verbose_name='CPU核心数')
class Meta: class Meta:
verbose_name = 'CPU高占用进程' verbose_name = 'CPU高占用进程'
verbose_name_plural = verbose_name verbose_name_plural = verbose_name
class HighGPUProcess(BaseProcessMonitor): class HighGPUProcess(BaseProcessMonitor):
"""GPU高占用进程监控""" """GPU高占用进程监控"""
gpu_usage = models.FloatField(default=0, verbose_name='GPU使用率') gpu_usage = models.FloatField(default=0, verbose_name='GPU使用率')
gpu_memory = models.FloatField(default=0, verbose_name='GPU显存使用量(MB)') gpu_memory = models.FloatField(default=0, verbose_name='GPU显存使用量(MB)')
gpu_index = models.IntegerField(default=0, verbose_name='GPU设备索引') gpu_index = models.IntegerField(default=0, verbose_name='GPU设备索引')
class Meta: class Meta:
verbose_name = 'GPU高占用进程' verbose_name = 'GPU高占用进程'
verbose_name_plural = verbose_name verbose_name_plural = verbose_name
class HighMemoryProcess(BaseProcessMonitor): class HighMemoryProcess(BaseProcessMonitor):
"""内存高占用进程监控""" """内存高占用进程监控"""
memory_usage = models.FloatField(default=0, verbose_name='内存使用量(GB)') memory_usage = models.FloatField(default=0, verbose_name='内存使用量(GB)')
memory_percent = models.FloatField(default=0, verbose_name='内存使用率') memory_percent = models.FloatField(default=0, verbose_name='内存使用率')
virtual_memory = models.FloatField(default=0, verbose_name='虚拟内存使用量(GB)') virtual_memory = models.FloatField(default=0, verbose_name='虚拟内存使用量(GB)')
swap_usage = models.FloatField(default=0, verbose_name='交换内存使用量(GB)') swap_usage = models.FloatField(default=0, verbose_name='交换内存使用量(GB)')
class Meta: class Meta:
verbose_name = '内存高占用进程' verbose_name = '内存高占用进程'
verbose_name_plural = verbose_name verbose_name_plural = verbose_name
class AllResourceProcess(models.Model): class AllResourceProcess(models.Model):
"""所有资源统计""" """所有资源统计"""
pid = models.IntegerField('进程ID') pid = models.IntegerField('进程ID')
process_name = models.CharField('进程名称', max_length=100) process_name = models.CharField('进程名称', max_length=100)
# CPU 相关 # CPU 相关
cpu_usage = models.FloatField('CPU使用率', default=0) cpu_usage = models.FloatField('CPU使用率', default=0)
cpu_user_time = models.FloatField('用户态CPU时间', default=0) cpu_user_time = models.FloatField('用户态CPU时间', default=0)
cpu_system_time = models.FloatField('系统态CPU时间', default=0) cpu_system_time = models.FloatField('系统态CPU时间', default=0)
# 内存相关 # 内存相关
memory_usage = models.FloatField('内存使用量(MB)', default=0) memory_usage = models.FloatField('内存使用量(MB)', default=0)
memory_percent = models.FloatField('内存使用率', default=0) memory_percent = models.FloatField('内存使用率', default=0)
virtual_memory = models.FloatField('虚拟内存(MB)', default=0) virtual_memory = models.FloatField('虚拟内存(MB)', default=0)
# GPU 相关 # GPU 相关
gpu_usage = models.FloatField('GPU使用率', default=0) gpu_usage = models.FloatField('GPU使用率', default=0)
gpu_memory = models.FloatField('GPU内存使用量(MB)', default=0) gpu_memory = models.FloatField('GPU内存使用量(MB)', default=0)
# 网络相关 # 网络相关
net_io_sent = models.FloatField('网络发送量(MB)', default=0) net_io_sent = models.FloatField('网络发送量(MB)', default=0)
net_io_recv = models.FloatField('网络接收量(MB)', default=0) net_io_recv = models.FloatField('网络接收量(MB)', default=0)
# 状态相关 # 状态相关
is_active = models.BooleanField('是否活跃', default=True) is_active = models.BooleanField('是否活跃', default=True)
status = models.IntegerField('状态', default=1) # 1:运行中, 0:已终止 status = models.IntegerField('状态', default=1) # 1:运行中, 0:已终止
log_file = models.CharField('日志文件路径', max_length=255, null=True) log_file = models.CharField('日志文件路径', max_length=255, null=True)
created_at = models.DateTimeField('创建时间', auto_now_add=True) created_at = models.DateTimeField('创建时间', auto_now_add=True)
updated_at = models.DateTimeField('更新时间', auto_now=True) updated_at = models.DateTimeField('更新时间', auto_now=True)
class Meta: class Meta:
db_table = 'all_resource_process' db_table = 'all_resource_process'
verbose_name = '全资源监控' verbose_name = '全资源监控'
verbose_name_plural = verbose_name verbose_name_plural = verbose_name
class TiktokUserVideos(models.Model):
"""TikTok用户视频信息"""
sec_user_id = models.CharField(max_length=255, unique=True)
nickname = models.CharField(max_length=255)
signature = models.TextField(blank=True, null=True)
follower_count = models.IntegerField(default=0)
total_favorited = models.IntegerField(default=0)
avatar_url = models.TextField(blank=True, null=True)
videos_folder = models.CharField(max_length=255)
video_paths = models.TextField(blank=True, null=True)
create_time = models.DateTimeField(auto_now_add=True)
update_time = models.DateTimeField(auto_now=True)
class Meta:
verbose_name = 'TikTok用户视频'
verbose_name_plural = 'TikTok用户视频'
def __str__(self):
return f"{self.nickname} ({self.sec_user_id})"

View File

@ -1,18 +1,20 @@
from django.urls import path from django.urls import path
from . import views from . import views
urlpatterns = [ urlpatterns = [
path('', views.index, name='index'), path('', views.index, name='index'),
path('api/process/<int:pid>/status/', views.get_process_status, name='get_process_status'), path('api/process/<int:pid>/status/', views.get_process_status, name='get_process_status'),
path('monitor/start/', views.start_monitor, name='start_monitor'), path('monitor/start/', views.start_monitor, name='start_monitor'),
path('monitor/stop/', views.stop_monitor, name='stop_monitor'), path('monitor/stop/', views.stop_monitor, name='stop_monitor'),
path('auto_detect/', views.auto_detect_high_resource_processes, name='auto_detect'), path('auto_detect/', views.auto_detect_high_resource_processes, name='auto_detect'),
path('stop_auto_detect/', views.stop_auto_detect, name='stop_auto_detect'), path('stop_auto_detect/', views.stop_auto_detect, name='stop_auto_detect'),
path('high-resource/', views.high_resource_list, name='high_resource_list'), path('high-resource/', views.high_resource_list, name='high_resource_list'),
path('scan-directory/', views.scan_directory, name='scan_directory'), path('scan-directory/', views.scan_directory, name='scan_directory'),
path('start/', views.start_monitor, name='start_monitor'), path('start/', views.start_monitor, name='start_monitor'),
path('stop/', views.stop_monitor, name='stop_monitor'), path('stop/', views.stop_monitor, name='stop_monitor'),
path('status/<int:pid>/', views.get_process_status, name='get_process_status'), path('status/<int:pid>/', views.get_process_status, name='get_process_status'),
path('stop-directory-monitor/', views.stop_directory_monitor, name='stop_directory_monitor'), path('stop-directory-monitor/', views.stop_directory_monitor, name='stop_directory_monitor'),
path('directory-status/', views.get_directory_status, name='directory_status'), path('directory-status/', views.get_directory_status, name='directory_status'),
] path('tiktok/user-videos/', views.get_tiktok_user_videos, name='get-tiktok-user-videos'),
path('api/tiktok/fetch_videos/', views.fetch_tiktok_videos, name='fetch_tiktok_videos'),
]

File diff suppressed because it is too large Load Diff

BIN
response.json Normal file

Binary file not shown.