import re
import time
import logging
import requests
import datetime
import os
import numpy as np
from PIL import Image
from lxml import etree
from injector.models import TaskModel
from injector.models import MediaInfoSyncModel
from domain.lsp.generated.anhuizx_xsd import Adi
from domain.lsp.base_injector import LspInjectorC2
from domain.lsp.value_object import C2ApiUtil, CodeFactory
from domain.injector.value_object import TaskStatusValue, ContentTypeValue
from domain.injector.value_object import DefinitionValue

from utils.date_utils import datetime_now

logger = logging.getLogger('django')


class GdYdXp(LspInjectorC2):

    def __init__(self, alias="GDYDXP"):
        super().__init__(alias)
        self.lsp_id = self.conf.lsp_id
        self.csp_id = self.conf.csp_id
        self.cms_api = self.conf.cms_api
        self.csp_name = self.conf.csp_name
        self.cms_id = "252730"
        self.code_factory = CodeFactory(self.csp_id)
        self.image_file_dir = "/home/sftp-user/vdb/xml_home"
        self.cast_sep = "|"
        self.genre_sep = ";"
        self.alias = alias
        self.resolution_num_mapper = {"QCIF": 1, "QVGA": 2, "2/3 D1": 3, "3/4 D1": 4, "D1": 5, "720P": 6, "1080i": 7,
                                      "1080P": 8, "4K(3840*2160)": 9, "4K(4096*2160)": 10}

    def get_genre(self, media_obj):
        genres = media_obj.genres
        if genres and len(genres):
            names = [genre["name"] for genre in genres]
            return self.genre_sep.join(names)
        return ""

    def get_content_type(self, content_type):
        return {
            ContentTypeValue.POSITIVE: "1",
            ContentTypeValue.CLIP: "3",
            ContentTypeValue.TRAILER: "2"
        }.get(content_type, "1")

    def _get_bit_rate_type(self, definition):
        if definition == DefinitionValue.P480:
            return 0
        elif definition == DefinitionValue.P720:
            return 1
        elif definition == DefinitionValue.P1080:
            return 2
        elif definition == DefinitionValue.K4:
            return 3
        return 0

    # 分辨率类型
    def _get_resolution(self, definition, width):
        def_mapper = {DefinitionValue.P320: "2/3 D1", DefinitionValue.P480: "D1", DefinitionValue.P720: "720P",
                      DefinitionValue.P1080: "1080P",
                      DefinitionValue.K4: "4K(3840*2160)" if width == 3840 else "4K(4096*2160)"}
        return self.resolution_num_mapper[def_mapper[definition]]

    def get_pic_desc(self, pic_type):
        PIC_TYPE_DESC_MAPPING = {
            2: "剧照：230x306",
            0: "海报：750x384",
            1: "缩略图：350x196",
            5: "广告图：710x400"
        }
        return PIC_TYPE_DESC_MAPPING.get(pic_type, 0)

    def _resize_pic(self, img, size):
        # 先创建一个目标大小的幕布，然后将放缩好的图片贴到中央，这样就省去了两边填充留白的麻烦。
        canvas = Image.new("RGB", size=size, color="#7777")
        target_width, target_height = size
        width, height = img.size
        offset_x = 0
        offset_y = 0
        if height > width:  # 高 是 长边
            height_ = target_height  # 直接将高调整为目标尺寸
            scale = height_ / height  # 计算高具体调整了多少，得出一个放缩比例
            width_ = int(width * scale)  # 宽以相同的比例放缩
            offset_x = (target_width - width_) // 2  # 计算x方向单侧留白的距离
        else:  # 同上
            width_ = target_width
            scale = width_ / width
            height_ = int(height * scale)
            offset_y = (target_height - height_) // 2

        img = img.resize((width_, height_), Image.BILINEAR)  # 将高和宽放缩
        canvas.paste(img, box=(offset_x, offset_y))  # 将放缩后的图片粘贴到幕布上
        # box参数用来确定要粘贴的图片左上角的位置。offset_x是x轴单侧留白，offset_y是y轴单侧留白，这样就能保证能将图片填充在幕布的中央

        return canvas

    def zoom_picture(self, file_path, pic_url, size):
        tmp_file_name = file_path.split("/")[-1]
        relative_file_dir = file_path.split(tmp_file_name)[0]

        name, suffix = re.split("\.", tmp_file_name)
        width, height = size

        tmp_file_path = os.path.join(self.image_file_dir, file_path)
        file_dir = tmp_file_path.split(tmp_file_name)[0]

        file_name = "{}_{}g{}.{}".format(name, width, height, suffix)
        file_path = os.path.join(file_dir, file_name)

        # if not os.path.exists(file_path):
        os.makedirs(file_dir, exist_ok=True)
        r = requests.get(pic_url)
        with open(tmp_file_path, "wb") as f:
            f.write(r.content)
        image = Image.open(tmp_file_path)
        # 打开指定路径的jpg图像文件
        # out = image.resize((width, height), Image.ANTIALIAS)
        out = self._resize_pic(image, (width, height))
        out.save(file_path)
        os.remove(tmp_file_path)

        return "{}{}".format(relative_file_dir, file_name)

    # 将图片地址转成ftp
    def to_picture_ftp_url(self, pic_url, size):
        file_path = re.split('/', pic_url, 3)[-1]
        # 判断图片是否存在，存在直接引用，不存在就创建
        file_path = self.zoom_picture(file_path, pic_url, size)
        storage_ftp_url = "ftp://{}:{}@{}:{}/{}".format("partner", "partner", "47.110.47.81",
                                                        "2121", file_path)
        return storage_ftp_url

    def bit_rate_transform(self, bit_rate):
        BIT_RATE_MAP = {1634.8: 1, 2150.4: 2, 8192: 3, 60: 4, 150: 5, 420: 6, 750: 7}
        BRM = [60, 150, 420, 750, 1634.8, 2150.4, 8192]
        _new_bit_poor = [abs(bit_rate - i) for i in BRM]
        min_poor = min(_new_bit_poor)
        min_bit = BRM[_new_bit_poor.index(min_poor)]
        return BIT_RATE_MAP.get(min_bit)

    def generate_code(self, _code, category):
        """
        安徽移动新发布code规范,规范{0}{1}{2}，不足补在{2}之前补0, 32位
        0：CMSID后5位
        1: 对象类型对应,见Object_map
        2: 自己平台编码
        """
        _OBJECT_MAP = {
            "Program": "001",
            "Movie": "002",
            "Cast": "003",
            "CastRoleMap": "004",
            "Channel": "005",
            "PhysicalChannel": "006",
            "Schedule": "007",
            "Picture": "008",
            "Category": "009",
            "Series": "010",
            "Package": "011"
        }
        after_list = _code.split("-")[:2]
        after = "".join(after_list).rjust(24 - len(self.csp_id), "0")
        in_the = _OBJECT_MAP[category]
        before = str(self.cms_id)[-5:]
        return "{}{}{}{}".format(before, in_the, self.csp_id, after)

    def v_codec_transform(self, v_codec):
        V_CODEC_MAP = {
            "h264": 1,  # H.264
            "mpeg4": 2,  # MPEG4
            "mpeg2video": 3  # MPEG2
        }
        return V_CODEC_MAP.get(v_codec, 2)

    def _get_category_desc(self, category):
        return "智慧幼托"

    def property_series(self, _id, volumn_count, media, action="REGIST"):
        search_name = self.transform_chinese_to_letter(self.get_media_name_by_alias(media))
        param_dict = {"category": media.category, "is_charge": 1}
        change_fields = self.injected_change.get_injected_fields(media.id, self.lsp_pk, param_dict)
        genres = self.get_genre(media) if media.genres else ""
        media_storage = media.mediastoragemodel_set.first()
        the_property = [
            {"Name": self.get_media_name_by_alias(media)},
            {"AnotherName": self.get_media_name_by_alias(media)},
            {"SeriesFlag": 1},
            {"Distributor": "无"},  # 发行公司 todo
            {"OriginalCountry": self.get_regions(media)},
            {"ProductionCompany": "无"},  # 出品公司 todo
            {"Language": media.languages},
            {"Awards": "其他"},  # 所获奖项 todo
            {"ContentFeatures": "2D"},  # 内容特色 2D、3D、VR、多机位、自由视角/子弹时间、其他 # todo
            {"Genre": genres},
            {"Rating": media.douban_score},
            {"DefinitionFlag": self._get_bit_rate_type(media_storage.definition)},
            {"CustContentType": self.get_content_type(media_storage.content_type)},
            {"ReleaseTime": media.date_published},  # 上映日期 TODO
            {"OrgAirStartDate": media.date_published},  # 首播日期 TODO
            {"ContentProvider": self.csp_name},
            {"Type": self._get_category_desc(media.category)},
            {"BusinessType": 0},
            {"BoxOffice": ""},
            {"IsSaneIP": ""},
            {"Tags": self.get_genre(media)},
            {"ReleaseYear": media.year},
            {"Keywords": "其他"},  # 关键字

            {"SearchName": search_name},
            {"OrgAirDate": "".join(media.date_published.split("-"))},
            {"VolumnCount": volumn_count},
            {"Status": 1},
            {"Description": media.storyline},
            {"ProgramType": self._get_category_desc(change_fields["category"])},
            {"ProgramType2": self.get_genre(media)},
            {"Reserve1": change_fields["is_charge"]},
            {"Bcharging": change_fields["is_charge"]},
            {"TitleSC": self.get_media_name_by_alias(media)},
            {"ScoreSC": media.douban_score}
        ]
        return {
            "element_type": "Series",
            "id": _id,
            "code": _id,
            "action": action,
            "property": the_property
        }

    def property_program(self, _id, name, media, media_storage, action="REGIST"):
        search_name = self.transform_chinese_to_letter(name.replace(" ", ""))
        param_dict = {"category": media.category, "is_charge": 1}
        change_fields = self.injected_change.get_injected_fields(media.id, self.lsp_pk, param_dict)
        genres = self.get_genre(media) if media.genres else ""
        return {
            "element_type": "Program",
            "id": _id,
            "code": _id,
            "action": action,
            "property": [
                {"Name": name},
                {"AnotherName": self.get_media_name_by_alias(media)},
                {"SeriesFlag": 0},
                {"Distributor": "无"},  # 发行公司 todo
                {"OriginalCountry": self.get_regions(media)},
                {"ProductionCompany": "无"},  # 出品公司 todo
                {"Language": media.languages},
                {"Awards": "其他"},  # 所获奖项 todo
                {"ContentFeatures": "2D"},  # 内容特色 2D、3D、VR、多机位、自由视角/子弹时间、其他 # todo
                {"Genre": genres},
                {"Rating": media.douban_score},
                {"DefinitionFlag": self._get_bit_rate_type(media_storage.definition)},
                {"CustContentType": self.get_content_type(media_storage.content_type)},
                {"ReleaseTime": media.date_published},  # 上映日期 TODO
                {"OrgAirStartDate": media.date_published},  # 首播日期日期 TODO
                {"ContentProvider": self.csp_name},
                {"Type": self._get_category_desc(media.category)},
                {"BusinessType": 0},
                {"BoxOffice": ""},
                {"IsSaneIP": ""},
                {"Tags": self.get_genre(media)},
                {"ReleaseYear": media.year},
                {"Keywords": "其他"},  # 关键字

                {"SearchName": search_name},
                {"Description": media.storyline},
                {"Status": 1},
                {"ProgramType": self._get_category_desc(change_fields["category"])},
                {"Bcharging": change_fields["is_charge"]},  # 0 免费 1 收费
                {"Reserve1": change_fields["is_charge"]},
                {"Chargingmode": "1"},
                {"ProgramType2": self.get_genre(media)},
                {"VolumnCount": media.episodes}
            ]
        }

    def format_duration_rate(self, seconds, frame):
        duration = time.strftime("%H%M%S", time.gmtime(seconds))
        return "{}{}".format(duration, frame)

    def property_movie(self, _id, media_storage, _type=1, action="REGIST"):
        return {
            "element_type": "Movie",
            "id": _id,
            "code": _id,
            "action": action,
            "property": [
                {"Type": _type},
                {"FileURL": self.get_storage_ftp_url(media_storage.url)},
                {"AudioType": self.get_audio_type(media_storage.a_codec)},
                {"ClosedCaptioning": 1},
                {"Duration": self.format_duration_rate(media_storage.seconds, media_storage.frame_rate)},
                {"BitRateType": self.bit_rate_transform(media_storage.bit_rate)},
                {"VideoType": self.v_codec_transform(media_storage.v_codec)},
                {"AudioFormat": 2},
                {"Resolution": self._get_resolution(media_storage.definition, media_storage.width)},
                {"SystemLayer": 1},
                {"MovieHeadDuration": 1},  # 片头时长 todo
                {"MovieTailDuration": 1},  # 片尾时长 todo
            ]
        }

    def property_picture(self, _id, file_url, size, pic_type, action="REGIST"):
        return {
            "element_type": "Picture",
            "id": _id,
            "code": _id,
            "action": action,
            "property": [
                {"FileURL": self.to_picture_ftp_url(file_url, size)},
                {"Description": self.get_pic_desc(pic_type)}
            ]
        }

    def _album_movie(self, media_storage, media, episode, media_ext, action="REGIST"):
        actors, directors, writers = self.get_media_person(media, actor=4, director=4, writer=2)
        vst, vet = self.get_episode_effective_time(media.id, episode.serial_num)

        _ppid = self.code_factory.gen_program_code(media_storage.id)
        ppid = self.generate_code(_ppid, "Program")
        p_program = self.property_program(ppid, self.get_media_name_by_alias(media), media, media_storage, action=action)
        p_program["property"].extend([
            {"ActorDisplay": actors},
            {"WriterDisplay": writers},
            {"DirectorDisplay": directors},
            {"LicensingWindowStart": vst},
            {"LicensingWindowEnd": vet},
            {"SeriesFlag": 0},
            {"vodType": 1},
            {"Kpeople": actors},
            {"Director": directors},
            {"Bpreview": self.get_content_type(media_storage.content_type)},
            {"PBS_Description": media_ext.focus}
        ])

        _pmid = self.code_factory.gen_movie_code(media_storage.id)
        pmid = self.generate_code(_pmid, "Movie")
        p_movie = self.property_movie(pmid, media_storage, action=action)

        object_pic, mapping_pic = self._general_object_pic(media_storage.id, media_ext.media_horpic,
                                                           media_ext.media_verpic,
                                                           ppid, action, "Program")
        mapping_movie = self.property_mapping("Program", "Movie", ppid, pmid, action=action)
        mapping_category = self.property_mapping("Category", "Program", "catcims2714", ppid, action=action)

        return {
            "domain": "2",
            "object_list": [p_program, p_movie] + object_pic,
            "mapping_list": [mapping_movie] + mapping_pic + [mapping_category],
        }

    def property_mapping_free(self, p_type, e_type, pid, eid, property_list=None, action="REGIST"):
        the_map = {
            "parent_type": p_type,
            "parent_id": pid,
            "element_type": e_type,
            "element_id": eid,
            "parent_code": pid,
            "element_code": eid,
            "action": action
        }
        if property_list:
            the_map["property"] = property_list
        return the_map

    def _album_series_header(self, media, media_ext, action):
        _episodes = int(media.episodes) if media.episodes else 0
        vst, vet = self.get_media_effective_time(media.id)
        actors, directors, writers = self.get_media_person(media, actor=4, director=4, writer=2)

        _psid = self.code_factory.gen_series_code(media.id)
        psid = self.generate_code(_psid, "Series")
        p_series = self.property_series(psid, _episodes, media, action=action)
        p_series["property"].extend([
            {"LicensingWindowStart": vst},
            {"LicensingWindowEnd": vet},
            {"PBS_Description": media_ext.focus},
            {"ActorDisplay": directors},
            {"WriterDisplay": actors},
            {"DirectorDisplay": directors},
            {"vodType": 2},
        ])

        object_pic, mapping_pic = self._general_object_pic(media.id, media_ext.media_horpic, media_ext.media_verpic,
                                                           psid, action)
        mapping_category = self.property_mapping("Category", "Series", "catcims2714", psid, action=action)

        return {
            "domain": "2",
            "object_list": [p_series] + object_pic,
            "mapping_list": mapping_pic + [mapping_category]
        }

    def _general_object_pic(self, media_id, media_horpic, media_verpic, psid, action, e_type="Series"):
        size_stagepic = self.image_resize.GDYDXP.get("stagepic")
        size_briefpic = self.image_resize.GDYDXP.get("briefpic")
        size_posterpic = self.image_resize.GDYDXP.get("posterpic")
        size_advertpic = self.image_resize.GDYDXP.get("advertpic")

        if e_type == "Series":
            stage_cvid = self.code_factory.gen_media_stage_pic_code(media_id)
            brief_cvid = self.code_factory.gen_media_breif_pic_code(media_id)
            poster_cvid = self.code_factory.gen_media_hpic_code(media_id)
            advert_cvid = self.code_factory.gen_media_advert_pic_code(media_id)
        else:
            stage_cvid = self.code_factory.gen_episode_picture_code(media_id)
            brief_cvid = self.code_factory.gen_episode_breif_pic_code(media_id)
            poster_cvid = self.code_factory.gen_episode_poster_pic_code(media_id)
            advert_cvid = self.code_factory.gen_episode_advert_pic_code(media_id)

        stage_code = self.generate_code(stage_cvid, "Picture")
        brief_code = self.generate_code(brief_cvid, "Picture")
        poster_code = self.generate_code(poster_cvid, "Picture")
        advert_code = self.generate_code(advert_cvid, "Picture")

        stage_series_picture = self.property_picture(stage_code, media_verpic, size_stagepic, 2, action=action)
        brief_series_picture = self.property_picture(brief_code, media_horpic, size_briefpic, 0, action=action)
        poster_series_picture = self.property_picture(poster_code, media_horpic, size_posterpic, 1, action=action)
        advert_series_picture = self.property_picture(advert_code, media_horpic, size_advertpic, 5, action=action)

        mapping_series_stage_picture = self.property_mapping("Picture", e_type, stage_code, psid, _type=2,
                                                             sequence=2, action=action)
        mapping_series_brief_picture = self.property_mapping("Picture", e_type, brief_code, psid, _type=0,
                                                             sequence=3, action=action)
        mapping_series_poster_picture = self.property_mapping("Picture", e_type, poster_code, psid, _type=1,
                                                              sequence=1, action=action)
        mapping_series_advert_picture = self.property_mapping("Picture", e_type, advert_code, psid, _type=5,
                                                              sequence=4, action=action)

        return [stage_series_picture, brief_series_picture, poster_series_picture, advert_series_picture], \
               [mapping_series_stage_picture, mapping_series_brief_picture, mapping_series_poster_picture,
                mapping_series_advert_picture]

    def get_episode_title(self, media, episode):
        title = ""
        if episode.title:
            title = self._have_episode_title(media, episode)
        else:
            title = self._nope_episode_title(media, episode)
        return title

    def _album_sub_series(self, media_storage, media, media_ext, episode, _episodes, action="REGIST"):
        actors, directors, writers = self.get_media_person(media, actor=4, director=4, writer=2)
        vst, vet = self.get_episode_effective_time(media.id, episode.serial_num)

        _psid = self.code_factory.gen_series_code(media.id)
        psid = self.generate_code(_psid, "Series")
        episode_title = self.get_episode_title(media, episode)

        _ppid = self.code_factory.gen_program_code(media_storage.id)
        ppid = self.generate_code(_ppid, "Program")
        p_program = self.property_program(ppid, episode_title, media, media_storage, action=action)
        p_program["property"].extend([
            {"SeriesFlag": 1},
            {"ActorDisplay": actors},
            {"WriterDisplay": writers},
            {"DirectorDisplay": directors},
            {"LicensingWindowStart": vst},
            {"LicensingWindowEnd": vet},
            {"Director": directors},
            {"vodType": 2},
            {"Bpreview": self.get_content_type(media_storage.content_type)},
            {"PBS_Description": media_ext.focus},
        ])

        object_pic, mapping_pic = self._general_object_pic(media_storage.id, episode.episode_horpic,
                                                           media_ext.media_verpic,
                                                           ppid, action, "Program")

        _pmid = self.code_factory.gen_movie_code(media_storage.id)
        pmid = self.generate_code(_pmid, "Movie")
        p_movie = self.property_movie(pmid, media_storage, action=action)
        mapping_movie = self.property_mapping("Program", "Movie", ppid, pmid, action=action)
        mapping_series = self.property_mapping("Series", "Program", psid, ppid, sequence=episode.serial_num,
                                               action=action)

        return {
            "domain": "2",
            "object_list": [p_program] + object_pic + [p_movie],
            "mapping_list": [mapping_series, mapping_movie] + mapping_pic,
        }

    def album(self, _header_id, action="REGIST"):
        header_id, _ = _header_id.split("_", 1)
        info_sync = MediaInfoSyncModel.objects.filter(pk=header_id).select_related("media").first()
        media = info_sync.media
        media_ext = media.mediaextfieldmodel
        action = info_sync.action_description()
        data = self._album_series_header(media, media_ext, action)
        xml = Adi.to_xml(data, flag="Nope")
        return xml

    def program(self, _task_id, action="REGIST"):
        task_id, _ = _task_id.split("_", 1)
        task = TaskModel.objects.filter(pk=task_id).select_related("storage").first()
        media_storage = task.storage  # 媒介
        media = media_storage.media  # 媒体文件主体
        episode = media_storage.episode  # (分集)扩展信息
        media_ext = media.mediaextfieldmodel  # 媒体文件补充信息

        _episodes = int(media.episodes) if media.episodes else 0
        if _episodes > 1:
            data = self._album_sub_series(media_storage, media, media_ext, episode, _episodes, action)
        else:  # 单剧集
            data = self._album_movie(media_storage, media, episode, media_ext, action)
        xml = Adi.to_xml(data, flag="Nope")
        return xml

    def exec_cmd(self, task_id):
        _task_id = self.generate_correlate_id(task_id)
        cmd_file_url = self.get_cmd_ftp_url(self.csp_id, _task_id)
        xml = C2ApiUtil.REQUESTXML.format(self.csp_id, self.lsp_id, "{}_{}".format(_task_id, datetime_now()),
                                          cmd_file_url)
        logger.info("{}_exec_cmd_request_xml: {}".format(self.alias, xml))
        headers = {"content-type": "text/xml", "SOAPAction": "application/soap+xml;charset=utf8"}
        response = requests.post(self.cms_api, data=xml, headers=headers).content
        logger.info("{}_exec_cmd_response: {}".format(self.alias, response))
        tree = etree.fromstring(response)

        code = tree.findall(".//multiRef")[1].text
        desc = tree.find(".//ErrorDescription").text
        if int(code) == 0:
            status = TaskStatusValue.INFO_TRANSFERING
            remark = ""
        else:
            status = TaskStatusValue.INFO_FAILED
            remark = desc

        TaskModel.objects.filter(pk=task_id).update(status=status, remark=remark)
        return status

    def exec_sync_header_cmd(self, sync):
        _sync_id = self.generate_correlate_id(sync.id, stage="series")
        cmd_file_url = self.get_cmd_ftp_url(self.csp_id, _sync_id)
        sync_id = "{}_{}".format(_sync_id, datetime_now())

        xml = C2ApiUtil.REQUESTXML.format(self.csp_id, self.lsp_id, sync_id, cmd_file_url)
        logger.info("{}_exec_sync_header_cmd_request_xml: {}".format(self.alias, xml))
        headers = {"content-type": "text/xml", "SOAPAction": "application/soap+xml;charset=utf8"}
        response = requests.post(self.cms_api, data=xml, headers=headers).content
        logger.info("{}_exec_sync_header_cmd_response: {}".format(self.alias, response))
        tree = etree.fromstring(response)
        code = tree.findall(".//multiRef")[1].text
        if int(code) == 0:
            status = MediaInfoSyncModel.SYNCING
        else:
            status = MediaInfoSyncModel.FAILED
        the_sync = MediaInfoSyncModel.objects.filter(pk=sync.id)
        if the_sync.first().status == MediaInfoSyncModel.PENDING:
            the_sync.update(status=status)
        return

    def injector(self, task):
        status, flag = 0, 0
        if task.status in (TaskStatusValue.FILE_PENDING, TaskStatusValue.INFO_PENDING):
            _task_id = self.generate_correlate_id(task.id)
            self.save_album_xml_file(self.csp_id, _task_id, param="program")
            status = self.exec_cmd(task.id)
            flag = self.get_continue_to_injector(status)
        return status, flag

    def update(self, task):  # 子集、单集 更新 REGIST 也可以
        if task.status in (TaskStatusValue.FILE_PENDING, TaskStatusValue.INFO_PENDING):
            _task_id = self.generate_correlate_id(task.id)
            self.save_album_xml_file(self.csp_id, _task_id, param="program", action="REGIST")
            self.exec_cmd(task.id)

    # 删除工单
    def remove(self, task):
        if task.status in (TaskStatusValue.FILE_PENDING, TaskStatusValue.INFO_PENDING):
            _task_id = self.generate_correlate_id(task.id)
            self.save_album_xml_file(self.csp_id, _task_id, param="program", action="DELETE")
            self.exec_cmd(task.id)
            self.off_line_storage(task.partner_id, task.storage_id)
            logger.warning("{} 下线媒体: {}, task_id: {}".format(self.partner_alias, task.storage_id, task.id))

    # 剧头注入
    def series_header_injector(self, sync):
        if sync.status == MediaInfoSyncModel.PENDING:
            header_id = self.generate_correlate_id(sync.id, stage="series")
            self.save_album_xml_file(self.csp_id, header_id, param="series")
            self.exec_sync_header_cmd(sync)

    def series_header_update(self, sync):
        self.series_header_injector(sync)

    # 删除工单
    def series_header_remove(self, sync):
        self.series_header_injector(sync)
