diff --git a/backend/task/src/task_config.py b/backend/task/src/task_config.py index 5c3dc433..9fd494e9 100644 --- a/backend/task/src/task_config.py +++ b/backend/task/src/task_config.py @@ -87,6 +87,13 @@ RESYNC_THUMBS: TaskItemConfig = { "api_stop": False, } +RESYNC_METADATA: TaskItemConfig = { + "title": "Sync Metadata to Media Files", + "group": "setting:thumbnailsync", + "api_start": True, + "api_stop": False, +} + INDEX_PLAYLISTS: TaskItemConfig = { "title": "Index Channel Playlist", "group": "channel:indexplaylist", @@ -119,6 +126,7 @@ TASK_CONFIG: dict[str, TaskItemConfig] = { "rescan_filesystem": RESCAN_FILESYSTEM, "thumbnail_check": THUMBNAIL_CHECK, "resync_thumbs": RESYNC_THUMBS, + "resync_metadata": RESYNC_METADATA, "index_playlists": INDEX_PLAYLISTS, "subscribe_to": SUBSCRIBE_TO, "version_check": VERSION_CHECK, diff --git a/backend/task/tasks.py b/backend/task/tasks.py index 64fb5ab8..995e1c85 100644 --- a/backend/task/tasks.py +++ b/backend/task/tasks.py @@ -24,6 +24,7 @@ from download.src.yt_dlp_handler import VideoDownloader from task.src.notify import Notifications from task.src.task_config import TASK_CONFIG from task.src.task_manager import TaskManager +from video.src.meta_embed import MetadataEmbed class BaseTask(Task): @@ -302,6 +303,19 @@ def re_sync_thumbs(self): ThumbFilesystem(task=self).embed() +@shared_task(bind=True, name="resync_metadata", base=BaseTask) +def re_sync_metadata(self): + """resync metadata to media files""" + manager = TaskManager() + if manager.is_pending(self): + print(f"[task][{self.name}] metadata re-embed is already running") + self.send_progress(["Metadata re-embed is already running."]) + return + + manager.init(self) + MetadataEmbed(task=self).embed() + + @shared_task(bind=True, name="subscribe_to", base=BaseTask) def subscribe_to(self, url_str: str, expected_type: str | bool = False): """ diff --git a/backend/video/src/meta_embed.py b/backend/video/src/meta_embed.py new file mode 100644 index 00000000..0a7f62e1 --- /dev/null +++ b/backend/video/src/meta_embed.py @@ -0,0 +1,51 @@ +"""bulk metadata embedding""" + +from common.src.es_connect import ElasticWrap, IndexPaginate +from video.src.index import YoutubeVideo + + +class MetadataEmbed: + """sync metadata to videos in bulk""" + + INDEX_NAME = "ta_video" + + def __init__(self, task=False): + self.task = task + + def embed(self): + """entry point""" + data = { + "query": {"match_all": {}}, + "_source": ["youtube_id"], + } + paginate = IndexPaginate( + index_name=self.INDEX_NAME, + data=data, + size=200, + callback=MetadataEmbedCallback, + task=self.task, + total=self._get_total(), + ) + _ = paginate.get_results() + + def _get_total(self): + """get total documents in index""" + path = f"{self.INDEX_NAME}/_count" + response, _ = ElasticWrap(path).get() + + return response.get("count") + + +class MetadataEmbedCallback: + """callback for metadata embed""" + + def __init__(self, source, index_name, counter=0): + self.source = source + self.index_name = index_name + self.counter = counter + + def run(self): + """run embed""" + for video in self.source: + youtube_id = video["_source"]["youtube_id"] + YoutubeVideo(youtube_id).embed_metadata() diff --git a/frontend/src/api/actions/updateTaskByName.ts b/frontend/src/api/actions/updateTaskByName.ts index 786c1a26..7053dca9 100644 --- a/frontend/src/api/actions/updateTaskByName.ts +++ b/frontend/src/api/actions/updateTaskByName.ts @@ -5,6 +5,7 @@ type TaskNamesType = | 'update_subscribed' | 'manual_import' | 'resync_thumbs' + | 'resync_metadata' | 'rescan_filesystem'; const updateTaskByName = async (taskName: TaskNamesType) => { diff --git a/frontend/src/pages/SettingsActions.tsx b/frontend/src/pages/SettingsActions.tsx index 8d7b44cf..a226019d 100644 --- a/frontend/src/pages/SettingsActions.tsx +++ b/frontend/src/pages/SettingsActions.tsx @@ -13,6 +13,7 @@ const SettingsActions = () => { const [deletePending, setDeletePending] = useState(false); const [processingImports, setProcessingImports] = useState(false); const [reEmbed, setReEmbed] = useState(false); + const [reSyncMeta, setReSyncMeta] = useState(false); const [backupStarted, setBackupStarted] = useState(false); const [isRestoringBackup, setIsRestoringBackup] = useState(false); const [reScanningFileSystem, setReScanningFileSystem] = useState(false); @@ -44,6 +45,7 @@ const SettingsActions = () => { deletePending || processingImports || reEmbed || + reSyncMeta || backupStarted || isRestoringBackup || reScanningFileSystem @@ -53,6 +55,7 @@ const SettingsActions = () => { setDeletePending(false); setProcessingImports(false); setReEmbed(false); + setReSyncMeta(false); setBackupStarted(false); setIsRestoringBackup(false); setReScanningFileSystem(false); @@ -104,6 +107,22 @@ const SettingsActions = () => { )} +
+

Embed metadata into media file

+

Embed metadata into media files as mp4 tags.

+
+ {reSyncMeta &&

Processing video metadata

} + {!reSyncMeta && ( +
+

ZIP file index backup