diff --git a/.drone.yml b/.drone.yml index ebe4a65..297b7c6 100644 --- a/.drone.yml +++ b/.drone.yml @@ -36,6 +36,27 @@ steps: depends_on: - install dependencies + - name: build container + image: quay.io/buildah/stable + when: + event: + - tag + commands: + - buildah login -u $DOCKER_USER -p $DOCKER_PASS -- $DOCKER_REGISTRY + - buildah manifest create ucast + - buildah bud --tag code.thetadev.de/hsa/ucast:latest --manifest ucast --arch amd64 -f deploy/Dockerfile . + - buildah bud --tag code.thetadev.de/hsa/ucast:latest --manifest ucast --arch arm64 -f deploy/Dockerfile . + - buildah manifest push --all ucast docker://code.thetadev.de/hsa/ucast:latest + environment: + DOCKER_REGISTRY: + from_secret: docker_registry + DOCKER_USER: + from_secret: docker_username + DOCKER_PASS: + from_secret: docker_password + depends_on: + - test + volumes: - name: cache temp: { } diff --git a/ucast/tasks/download.py b/ucast/tasks/download.py index be14ddb..d3bbe81 100644 --- a/ucast/tasks/download.py +++ b/ucast/tasks/download.py @@ -3,6 +3,7 @@ import os from django.db.models import ObjectDoesNotExist from django.utils import timezone +from yt_dlp.utils import DownloadError from ucast import queue from ucast.models import Channel, Video @@ -21,7 +22,25 @@ def _load_scraped_video(vid: youtube.VideoScraped, channel: Channel): try: video = Video.objects.get(video_id=vid.id) except ObjectDoesNotExist: - details = youtube.get_video_details(vid.id) + try: + details = youtube.get_video_details(vid.id) + except DownloadError as e: + if "available" in e.msg: + # Create dummy video to prevent further download attempts + # of unavailable videos + video = Video( + video_id=vid.id, + title="", + slug="", + channel=channel, + published=timezone.datetime(2000, 1, 1, tzinfo=timezone.utc), + description="", + duration=0, + is_deleted=True, + ) + video.save() + return + raise e # Dont load active livestreams if details.is_currently_live: @@ -50,20 +69,23 @@ def _load_scraped_video(vid: youtube.VideoScraped, channel: Channel): and video.is_deleted is False and channel.should_download(video) ): - queue.enqueue(download_video, video) + queue.enqueue(download_video, video.id) redis.delete(lock_key) -def download_video(video: Video): +def download_video(v_id: int): """ Download a video including its thumbnail, create a cover image and store everything in the channel folder. - :param video: Video object + :param v_id: Video ID """ # Return if the video was already downloaded by a previous task - video.refresh_from_db() + try: + video = Video.objects.get(id=v_id) + except ObjectDoesNotExist: + return if video.downloaded: return @@ -71,7 +93,14 @@ def download_video(video: Video): channel_folder = store.get_or_create_channel_folder(video.channel.slug) audio_file = channel_folder.get_audio(video.slug) - details = youtube.download_audio(video.video_id, audio_file) + try: + details = youtube.download_audio(video.video_id, audio_file) + except DownloadError as e: + if "available" in e.msg: + video.is_deleted = True + video.save() + return + raise e # Download/convert thumbnails tn_path = channel_folder.get_thumbnail(video.slug) @@ -106,8 +135,12 @@ def download_video(video: Video): video.save() -def update_channel(channel: Channel): +def update_channel(c_id: int): """Update a single channel from its RSS feed""" + try: + channel = Channel.objects.get(id=c_id) + except ObjectDoesNotExist: + return videos = youtube.get_channel_videos_from_feed(channel.channel_id) for vid in videos: @@ -123,18 +156,23 @@ def update_channels(): This task is scheduled a regular intervals. """ for channel in Channel.objects.filter(active=True): - queue.enqueue(update_channel, channel) + queue.enqueue(update_channel, channel.id) -def download_channel(channel: Channel, limit: int): +def download_channel(c_id: int, limit: int): """ Download maximum number of videos from a channel. - :param channel: Channel object + :param c_id: Channel ID (Database) :param limit: Max number of videos """ if limit < 1: return + try: + channel = Channel.objects.get(id=c_id) + except ObjectDoesNotExist: + return + for vid in youtube.get_channel_videos_from_scraper(channel.channel_id, limit): _load_scraped_video(vid, channel) diff --git a/ucast/tasks/library.py b/ucast/tasks/library.py index b6b3481..797ea49 100644 --- a/ucast/tasks/library.py +++ b/ucast/tasks/library.py @@ -1,5 +1,6 @@ import os +from django.db.models import ObjectDoesNotExist from django.utils import timezone from ucast import queue @@ -7,7 +8,12 @@ from ucast.models import Channel, Video from ucast.service import cover, storage, util, videoutil, youtube -def recreate_cover(video: Video): +def recreate_cover(v_id: int): + try: + video = Video.objects.get(id=v_id) + except ObjectDoesNotExist: + return + store = storage.Storage() cf = store.get_channel_folder(video.channel.slug) @@ -42,7 +48,7 @@ def recreate_cover(video: Video): def recreate_covers(): for video in Video.objects.filter(downloaded__isnull=False): - queue.enqueue(recreate_cover, video) + queue.enqueue(recreate_cover, video.id) def update_file_storage(): @@ -81,7 +87,12 @@ def update_file_storage(): video.save() -def update_channel_info(channel: Channel): +def update_channel_info(ch_id: int): + try: + channel = Channel.objects.get(id=ch_id) + except ObjectDoesNotExist: + return + channel_data = youtube.get_channel_metadata( youtube.channel_url_from_id(channel.channel_id) ) @@ -104,4 +115,4 @@ def update_channel_info(channel: Channel): def update_channel_infos(): for channel in Channel.objects.filter(active=True): - queue.enqueue(update_channel_info, channel) + queue.enqueue(update_channel_info, channel.id) diff --git a/ucast/templates/ucast/downloads.html b/ucast/templates/ucast/downloads.html index 9cbe830..9f51790 100644 --- a/ucast/templates/ucast/downloads.html +++ b/ucast/templates/ucast/downloads.html @@ -27,11 +27,15 @@