国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【Python 自動化】自媒體剪輯第一版·思路簡述與技術方案

這篇具有很好參考價值的文章主要介紹了【Python 自動化】自媒體剪輯第一版·思路簡述與技術方案。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

大家都知道我主業(yè)是個運維開發(fā)(或者算法工程師),每天時間不多,但我又想做自媒體。然后呢,我就想了個方案,每天起來之后寫個短視頻的腳本,包含一系列圖片和文字,然后上班的時候給它提交到流水線上跑,下班之前就能拿到視頻,然后往各大平臺上一傳,是不是挺美滋滋的。

我和我之前的合伙人一說,他就覺得做短視頻沒啥用,不過還是按我說的做,出了一個腳本,我一看什么玩意兒,根本就沒辦法跑起來。無奈之下,我重新寫了現(xiàn)在我要展示的這個版本。后來這個合伙人就裝逼失敗跑路了,大快人心。這個版本不長,也就3~400行,我就在想,連一個幾百行的程序都寫不好,還談啥副業(yè),真是可笑。

首先程序接受這樣一個 YAML 劇本,定義了圖片和文字內(nèi)容。這里我把MR數(shù)據(jù)楊第一版引擎所使用的素材做成了劇本:

name: 人氣直播主饒方晴氣質笑顏無害又迷人
format: mp4
imgSize: [1280, 768]
contents:
  - type: image:file
    value: asset/1.jpg
  - type: audio:tts
    value: 人氣直播主饒方晴,氣質笑顏無害又迷人。
  - type: image:file
    value: asset/2.jpg
  - type: audio:tts
    value: 這笑容是要融化多少單身漢!小編今天要為大家送上這位白嫩系的正妹「亮亮Sunny」。
  - type: image:file
    value: asset/3.jpg
  - type: audio:tts
    value: 渾身白皙的肌膚吹彈可破一般稚嫩,留著柔順長發(fā)的她襯著那張清新又甜美的臉蛋。
  - type: image:file
    value: asset/4.jpg
  - type: audio:tts
    value: 水汪大眼搭上迷人的微笑,讓人對到一眼就深深著迷。
  - type: image:file
    value: asset/5.jpg
  - type: audio:tts
    value: 往下一望還有一雙壯美的峰景,深厚的事業(yè)線簡直不留余地給司機們?。?  - type: image:file
    value: asset/6.jpg
  - type: audio:tts
    value: 亮亮本名又叫作「饒方晴」,身為美女主播的她憑著那亮麗的外表和姣好的體態(tài)。
  - type: image:file
    value: asset/7.jpg
  - type: audio:tts
    value: 總能無時無刻圈下不少忠實粉絲,開朗又活潑的個性更是深受大家喜愛。
  - type: image:file
    value: asset/8.jpg
  - type: audio:tts
    value: 大方的她平日也總在IG上放曬迷人又性感的自拍照。
  - type: image:file
    value: asset/9.jpg
  - type: audio:tts
    value: 不只是濠乳溝乍現(xiàn),連筆直又纖長的美腿也讓腿控們大飽眼福呢!

很簡單是吧,就是一個圖片一個文本這樣。真的不用太多,精雕細琢沒意義,樊登說了,自媒體平臺靠走量刷流量池取勝。我在此奉勸大家徹底拋棄完美主義。

子命令回調

因為我這個功能是一個工具集的子命令,回調就是子命令的入口。我們看看回調咋寫的:

def autovideo(args):
    cfg_fname = args.config
    if not cfg_fname.endswith('.yml'):
        print('請?zhí)峁?YAML 文件')
        return
    cfg_dir = path.dirname(cfg_fname)
    user_cfg = yaml.safe_load(open(cfg_fname, encoding='utf8').read())
    update_config(user_cfg, cfg_dir)
        
    # 素材預處理
    preproc_asset(config)
    # 轉換成幀的形式
    frames = contents2frame(config['contents'])
    # 組裝視頻
    video = make_video(frames)
    if config['format'] != 'mp4':
        video = ffmpeg_conv_fmt(video, 'mp4',  config['format'])
    # 寫文件
    video_fname = fname_escape(config['name']) + '.' + config['format']
    print(video_fname)
    open(video_fname, 'wb').write(video)

一共這么五步:(1)讀取配置(2)預處理(3)模塊劃分(4)組裝(5)寫文件,大功告成。

讀取配置

(1)將用戶傳入的配置覆蓋程序默認配置,(2)將所有素材對于劇本的相對路徑轉換成對于 CWD 的相對路徑,(3)加載外部模塊,覆蓋 TTI 和 TTS 函數(shù)。

def update_config(user_cfg, cfg_dir):
    global tts
    global tti
    
    config.update(user_cfg)
    if not config['contents']:
        raise AttributeError('內(nèi)容為空,無法生成')
        
    for cont in config['contents']:
        if cont['type'].endswith(':file'):
            cont['value'] = path.join(cfg_dir, cont['value'])
    if config['header']:
        config['header'] = path.join(cfg_dir, config['header'])
    if config['footer']:
        config['footer'] = path.join(cfg_dir, config['footer'])
        
    if config['external']:
        mod_fname = path.join(cfg_dir, config['external'])
        exmod = load_module(mod_fname)
        if hasattr(exmod, 'tts'): tts = exmod.tts
        if hasattr(exmod, 'tti'): tti = exmod.tti

預處理

(1)遍歷每個內(nèi)容,分別處理不同類型:

  • :file是文件,直接讀取
  • image:dir需要從資源目錄隨機挑選一張指定關鍵詞的圖片
  • :url是網(wǎng)絡資源,直接下載
  • audio:tts是機器朗讀的音頻,直接調用 TTS 函數(shù)
  • image:color是純色圖片,用 OpenCV 生成
  • image:tti是文成圖,直接調用 TTI 函數(shù)
  • audio:blank是空白音頻,直接用 SciPy 生成

(2)剪裁圖片到配置規(guī)定的尺寸,(3)如果第一張不是圖片,將第一個圖片提前(當前這塊邏輯也可以改成如果第一張不是圖片就插入一個純黑的圖片)。

def preproc_asset(config):
    # 加載或生成內(nèi)容
    for cont in config['contents']:
        if cont['type'].endswith(':file'):
            cont['asset'] = open(cont['value'], 'rb').read()
        elif cont['type'] == 'image:dir':
            assert config['assetDir']
            cont['asset'] = get_rand_asset_kw(config['assetDir'], cont['value'], is_pic)
        elif cont['type'].endswith(':url'):
            url = cont['value']
            print(f'下載:{url}')
            cont['asset'] = request_retry('GET', url).content
        elif cont['type'] == 'audio:tts':
            text = cont['value']
            print(f'TTS:{text}')
            cont['asset'] = tts(text)
        elif cont['type'] == 'image:color':
            bgr = cont['value']
            if isinstance(bgr, str):
                assert re.search(r'^#[0-9a-fA-F]{6}$', bgr)
                r, g, b = int(bgr[1:3], 16), int(bgr[3:5], 16), int(bgr[5:7], 16)
                bgr = [b, g, r]
            cont['asset'] = gen_mono_color(config['size'][0], config['size'][1], bgr)
        elif cont['type'] == 'image:tti':
            text = cont['value']
            print(f'TTI:{text}')
            cont['asset'] = tti(text)
        elif cont['type'] == 'audio:blank':
            cont['asset'] = gen_blank_audio(cont['value'])
            
    config['contents'] = [
        c for c in config['contents']
        if 'asset' in c
    ]
    
    # 剪裁圖片
    w, h = config['size']
    mode = config['resizeMode']
    for c in config['contents']:
        if c['type'].startswith('image:'):
            c['asset'] = resize_img(c['asset'], w, h, mode)
    
    # 如果第一張不是圖片,則提升第一個圖片
    idx = -1
    for i, c in enumerate(config['contents']):
        if c['type'].startswith('image:'):
            idx = i
            break
    if idx == -1:
        print('內(nèi)容中無圖片,無法生成視頻')
        sys.exit()
    if idx != 0:
        c = config['contents'][idx]
        del config['contents'][idx]
        config['contents'].insert(0, c)

模塊劃分

每個模塊相對獨立,每個模塊只能有一個圖片,但可以有多個音頻。所以將單個圖片和其后的連續(xù)音頻劃分到一個模塊中。每個模塊單獨組裝,之后再連接起來,這樣比較方便處理字幕。

def contents2frame(contents):
    frames = []
    for c in contents:
        if c['type'].startswith('image:'):
            frames.append({
                'image': c['asset'],
                'audios': [],
            })
        elif c['type'].startswith('video:'):
            frames.append({
                'video_noaud': c['asset'],
                'audios': [],
            })
        elif c['type'].startswith('audio:'):
            if len(frames) == 0: continue
            frames[-1]['audios'].append({
                'audio': c['asset'],
                'len': audio_len(c['asset']),
                'subtitle': c['value'] if c['type'] == 'audio:tts' else '',
            })
    for f in frames:
        f['len'] = sum([a['len'] for a in f['audios']])
        f['video_noaud'] = img_nsec_2video(f['image'], f['len'], config['fps'])
        f['audio'] = (
            f['audios'][0]['audio'] 
            if len(f['audios']) == 1 
            else ffmpeg_cat([a['audio'] for a in f['audios']], 'mp3')
        )
        f['video'] = ffmpeg_merge_video_audio(f['video_noaud'], f['audio'], audio_fmt='mp3')
        f['srt'] = gen_srt(f['audios'])
        f['video'] = ffmpeg_add_srt(f['video'], f['srt'])
    return frames

這里的邏輯是,(1)劃分模塊,將每個圖片連同后面的連續(xù)音頻劃分進一個模塊。

(2)對于每個模塊,根據(jù)音頻求出整個模塊長度,制作無聲視頻,連接音頻,制作字幕,然后把這些都連接起來。

字幕處理

模塊劃分這一步需要為每個模塊生成 SRT 文件。


def srt_time_fmt(num):
    sec = int(num) % 60
    min_ = int(num) // 60 % 60
    hr = int(num) // 3600
    msec = int(num * 1000) % 1000
    return f'{hr:02d}:{min_:02d}:{sec:02d},{msec:03d}'

# 生成字幕
def gen_srt(audios):
    # 提取 audios 數(shù)組中的字幕
    subs = [
        {
            'text': a['subtitle'],
            'len': a['len'],
        }
        for a in audios
    ]
    # 將每個字幕按指定長度分割
    for s in subs:
        text = s['text']
        if not text: continue
        parts = split_text_even(text, config['subtitleMaxLen'])
        s['parts'] = [
            {
                'text': p,
                'len': len(p) / len(text) * s['len'],
            }
            for p in parts
        ]
    # 將分割后的字幕替換原字幕
    subs = sum([
        s.get('parts', s) for s in subs
    ], [])
    # 計算起始時間
    offset = 0
    for s in subs:
        s['start'] = offset
        offset += s['len']
    # 組裝 SRT 文件
    srts = []
    for i, s in enumerate(subs):
        if not s['text']: continue
        st, ed = srt_time_fmt(s['start']), srt_time_fmt(s['start'] + s['len'])
        text = s['text']
        srts.append(f'{i+1}\n{st} --> {ed}\n{text}\n')
    srt = '\n'.join(srts)
    return srt

這里我們把每個音頻挑出來,從里面獲取字幕和長度。然后對于每個字幕,將其按照指定好的長度分割,按照每個片段的比例計算其長度。最后給每條字幕計算起始時間,并組裝 SRT 文件。

為了防止字母分割之后有個小尾巴,我們根據(jù)最大長度計算片段數(shù)量,然后按照這個數(shù)量平均分割。

def split_text_even(text, maxlen):
    textlen = len(text)
    num = math.ceil(textlen / maxlen)
    reallen = textlen // num
    res = [text[i:i+reallen] for i in range(0, textlen, reallen)]
    if textlen % num != 0:
        res[-1] += text[:-textlen%num]
    return res

組裝視頻

這就簡單了,合并所有模塊的視頻,并添加片頭片尾(如果存在的話)。

def make_video(frames):
    # 合并視頻
    video = ffmpeg_cat([f['video'] for f in frames])
    # 合并片頭片尾
    if config['header']:
        header = open(config['header'], 'rb').read()
        video = ffmpeg_cat([header, video])
    if config['footer']:
        footer = open(config['footer'], 'rb').read()
        video = ffmpeg_cat([video, footer])
    return video

工具函數(shù)

工具函數(shù)基本都用 FFMPEG 或者 OpenCV 實現(xiàn)的。

tts

直接調用 EdgeTTS:

def edgetts_cli(text, voice='zh-CN-XiaoyiNeural', fmt='mp3'):
    fname = path.join(tempfile.gettempdir(), uuid.uuid4().hex + '.' + fmt)
    cmd = [
        'edge-tts', '-t', text, '-v', voice, '--write-media', fname,
    ]
    print(f'cmd: {cmd}')
    subp.Popen(cmd, shell=True).communicate()
    res = open(fname, 'rb').read()
    safe_remove(fname)
    return res

然后外面包了一層加了個緩存:

def tts(text):
    hash_ = hashlib.md5(text.encode('utf8')).hexdigest()
    cache = load_tts(hash_, 'none')
    if cache: return cache
    data = edgetts_cli(text)
    save_tts(hash_, 'none', data)
    return data
	
def load_tts(hash_, voice):
    fname = path.join(DATA_DIR, f'{hash_}-{voice}')
    if path.isfile(fname):
        return open(fname, 'rb').read()
    else:
        return None
        
def save_tts(hash_, voice, data):
    safe_mkdir(DATA_DIR)
    fname = path.join(DATA_DIR, f'{hash_}-{voice}')
    open(fname, 'wb').write(data)

tti

這個函數(shù)沒有實現(xiàn),可以在外部腳本里面實現(xiàn)。

get_rand_asset_kw

os.walk遍歷指定目錄及其子目錄中的文件,使用過濾函數(shù)和關鍵詞過濾,再隨機挑選。

def get_rand_asset_kw(dir, kw, func_filter=is_pic):
    tree = list(os.walk(dir))
    fnames = [path.join(d, n) for d, _, fnames in tree for n in fnames]
    pics = [n for n in fnames if func_filter(n)]
    cand = [n for n in pics if kw in n]
    return random.choice(cand) if len(cand) else  random.choice(pics)

gen_mono_color

用 NumPy 生成[H, W, 3]尺寸的 BGR 圖片,然后每個第二維都賦值為指定 BGR。最后拿 OpenCV 編碼。

def gen_mono_color(w, h, bgr):
    assert len(bgr) == 3
    img = np.zeros([h, w, 3])
    img[:, :] = bgr
    img = cv2.imencode('.png', img, [cv2.IMWRITE_PNG_COMPRESSION, 9])[1]
    return bytes(img)

gen_blank_audio

用 NumPy 生成[SR * L]尺寸的純零數(shù)組,用sp.io寫到內(nèi)存流里面。

def gen_blank_audio(nsec, sr=22050, fmt='wav'):
    audio = np.zeros(int(nsec * sr), dtype=np.uint8)
    bio = BytesIO()
    wavfile.write(bio, sr, audio)
    audio = bio.getvalue()
    if fmt != 'wav':
        audio = ffmpeg_conv_fmt(audio, 'wav', fmt)
    return audio

ffmpeg_get_info

調用 FFMPEG 獲取視頻的時長、FPS、SR。

def ffmpeg_get_info(video, fmt='mp4'):
    if isinstance(video, bytes):
        fname = path.join(tempfile.gettempdir(), uuid.uuid4().hex + '.' + fmt)
        open(fname, 'wb').write(video)
    else:
        fname = video
    cmd = ['ffmpeg', '-i', fname]
    print(f'cmd: {cmd}')
    r = subp.Popen(
            cmd, stdout=subp.PIPE, stderr=subp.PIPE, shell=True
    ).communicate()
    text = r[1].decode('utf8')
    res = {}
    m = re.search(r'Duration:\x20(\d+):(\d+):(\d+)(.\d+)', text)
    if m:
        hr = int(m.group(1))
        min_ = int(m.group(2))
        sec = int(m.group(3))
        ms = float(m.group(4))
        res['duration'] = hr * 3600 + min_ * 60 + sec + ms
    m = re.search(r'(\d+)\x20fps', text)
    if m:
        res['fps'] = int(m.group(1))
    m = re.search(r'(\d+)\x20Hz', text)
    if m:
        res['sr'] = int(m.group(1))
    if isinstance(video, bytes):
        safe_remove(fname)

resize_img

保持長寬比縮放圖片。有兩種模式:wrap將圖片縮放到不大于指定尺寸的最大尺寸,然后填充不足的部分;fill縮放到大于指定尺寸的最小尺寸,然后切掉多余的部分。

# 縮放到最小填充尺寸并剪裁
def resize_img_fill(img, nw, nh):
    fmt_bytes = isinstance(img, bytes)
    if fmt_bytes:
        img = cv2.imdecode(np.frombuffer(img, np.uint8), cv2.IMREAD_COLOR)
    h, w, *_ = img.shape
    # 計算寬高的縮放比例,使用較大值等比例縮放
    x_scale = nw / w
    y_scale = nh / h
    scale = max(x_scale, y_scale)
    rh, rw = int(h * scale), int(w * scale)
    img = cv2.resize(img, (rw, rh), interpolation=cv2.INTER_CUBIC)
    # 剪裁成預定大小
    cut_w = rw - nw
    cut_h = rh - nh
    img = img[
        cut_h // 2 : cut_h // 2 + nh,
        cut_w // 2 : cut_w // 2 + nw,
    ]
    if fmt_bytes:
        img = bytes(cv2.imencode('.png', img, IMWRITE_PNG_FLAG)[1])
    return img

# 縮放到最大包圍并填充
def resize_img_wrap(img, nw, nh):
    fmt_bytes = isinstance(img, bytes)
    if fmt_bytes:
        img = cv2.imdecode(np.frombuffer(img, np.uint8), cv2.IMREAD_COLOR)
    h, w, *_ = img.shape
    # 計算寬高的縮放比例,使用較小值等比例縮放
    x_scale = nw / w
    y_scale = nh / h
    scale = min(x_scale, y_scale)
    rh, rw = int(h * scale), int(w * scale)
    img = cv2.resize(img, (rw, rh), interpolation=cv2.INTER_CUBIC)
    # 填充到預定大小
    pad_w = nw - rw
    pad_h = nh - rh
    img = cv2.copyMakeBorder(
        img, pad_h // 2, pad_h - pad_h // 2, pad_w // 2, pad_w - pad_w // 2, 
        cv2.BORDER_CONSTANT, None, (0,0,0)
    ) 
    if fmt_bytes:
        img = bytes(cv2.imencode('.png', img, IMWRITE_PNG_FLAG)[1])
    return img
    
def resize_img(img, nw, nh, mode='wrap'):
    assert mode in ['wrap', 'fill']
    func_resize_img = resize_img_wrap if mode == 'wrap' else resize_img_fill
    return func_resize_img(img, nw, nh)

imgs_nsecs_2video

將指定圖片做成長度固定的視頻。就是使用秒數(shù)乘以 FPS,作為幀數(shù),將幀塞進VideoWriter做成視頻即可。

def imgs2video(imgs, fps=30):
    ofname = path.join(tempfile.gettempdir(), uuid.uuid4().hex + '.mp4')
    fmt = cv2.VideoWriter_fourcc('M', 'P', '4', 'V')
    w, h = get_img_size(imgs[0])
    vid = cv2.VideoWriter(ofname, fmt, fps, [w, h])
    for img in imgs:
        if isinstance(img, bytes):
            img = cv2.imdecode(np.frombuffer(img, np.uint8), cv2.IMREAD_COLOR)
        vid.write(img)
    vid.release()
    res = open(ofname, 'rb').read()
    safe_remove(ofname)
    return res
    
def get_img_size(img):
    if isinstance(img, bytes):
        img = cv2.imdecode(np.frombuffer(img, np.uint8), cv2.IMREAD_COLOR)
    assert isinstance(img, np.ndarray) and img.ndim in [2, 3]
    return img.shape[1], img.shape[0]
    
def img_nsec_2video(img, nsec, fps=30):
    count = math.ceil(fps * nsec)
    imgs = [img] * count
    return imgs2video(imgs, fps)

ffmpeg_*

這些都是 FFMPEG 命令行的包裝,注意處理好編碼和反斜杠什么的就可以。

def ffmpeg_conv_fmt(video, from_, to):
    prefix = uuid.uuid4().hex
    from_fname = path.join(tempfile.gettempdir(), f'{prefix}.{from_}')
    to_fname = path.join(tempfile.gettempdir(), f'{prefix}.{to}')
    open(from_fname, 'wb').write(video)
    cmd = ['ffmpeg', '-i', from_fname, '-c', 'copy', to_fname, '-y']
    print(f'cmd: {cmd}')
    subp.Popen(cmd, shell=True).communicate()
    res = open(to_fname, 'rb').read()
    safe_remove(from_fname)
    safe_remove(to_fname)
    return res

def ffmpeg_cat(videos, fmt='mp4'):
    tmpdir = path.join(tempfile.gettempdir(), uuid.uuid4().hex)
    safe_mkdir(tmpdir)
    for i, video in enumerate(videos):
        fname = path.join(tmpdir, f'{i}.{fmt}')
        open(fname, 'wb').write(video)
    video_fnames = [
        'file ' + path.join(tmpdir, f'{i}.{fmt}').replace('\\', '\\\\')
        for i in range(len(videos))
    ]
    video_li_fname = path.join(tmpdir, f'list.txt') 
    open(video_li_fname, 'w', encoding='utf8').write('\n'.join(video_fnames))
    ofname = path.join(tmpdir, f'res.{fmt}')
    cmd = [
        'ffmpeg', '-f', 'concat', '-safe', '0',
        '-i', video_li_fname, '-c', 'copy', ofname, '-y',
    ]
    print(f'cmd: {cmd}')
    subp.Popen(cmd, shell=True).communicate()
    res = open(ofname, 'rb').read()
    safe_rmdir(tmpdir)
    return res

def ffmpeg_add_srt(video, srt, video_fmt='mp4'):
    tmpdir = path.join(tempfile.gettempdir(), uuid.uuid4().hex)
    safe_mkdir(tmpdir)
    vfname = path.join(tmpdir, f'video.{video_fmt}')
    open(vfname, 'wb').write(video)
    sfname = path.join(tmpdir, f'subtitle.srt')
    open(sfname, 'w', encoding='utf8').write(srt)
    res_fname = path.join(tmpdir, f'merged.{video_fmt}')
    cmd = ['ffmpeg', '-i', f'video.{video_fmt}', '-vf', f'subtitles=subtitle.srt', res_fname, '-y']
    '''
    cmd = [
        'ffmpeg', '-i', vfname, '-i', sfname, 
        '-c', 'copy', res_fname, '-y',
    ]
    if video_fmt == 'mp4': cmd += ['-c:s', 'mov_text']
    '''
    print(f'cmd: {cmd}')
    subp.Popen(cmd, shell=True, cwd=tmpdir).communicate()
    res = open(res_fname, 'rb').read()
    safe_rmdir(tmpdir)
    return res

def ffmpeg_merge_video_audio(video, audio, video_fmt='mp4', audio_fmt='mp4'):
    tmpdir = path.join(tempfile.gettempdir(), uuid.uuid4().hex)
    safe_mkdir(tmpdir)
    vfname = path.join(tmpdir, f'video.{video_fmt}')
    v0fname = path.join(tmpdir, f'video0.{video_fmt}')
    open(vfname, 'wb').write(video)
    afname = path.join(tmpdir, f'audio.{audio_fmt}')
    a0fname = path.join(tmpdir, f'audio0.{audio_fmt}')
    open(afname, 'wb').write(audio)
    res_fname = path.join(tmpdir, f'merged.{video_fmt}')
    cmds = [
        ['ffmpeg', '-i', vfname, '-vcodec', 'copy', '-an', v0fname, '-y'],
        ['ffmpeg', '-i', afname, '-acodec', 'copy', '-vn', a0fname, '-y'],
        ['ffmpeg', '-i', a0fname, '-i', v0fname, '-c', 'copy', res_fname, '-y'],
    ]
    for cmd in cmds:
        print(f'cmd: {cmd}')
        subp.Popen(cmd, shell=True).communicate()
    res = open(res_fname, 'rb').read()
    safe_rmdir(tmpdir)
    return res

load_module

用一些騷操作加載外部模塊。(1)創(chuàng)建加載目錄,并添加到sys.path。(2)為模塊起個名字,并將文件內(nèi)容用這個名字保存到加載目錄中。(3)導入模塊,刪除文件。文章來源地址http://www.zghlxwxcb.cn/news/detail-701402.html

def load_module(fname):
    if not path.isfile(fname) or \
        not fname.endswith('.py'):
        raise FileNotFoundError('外部模塊應是 *.py 文件')
    tmpdir = path.join(tempfile.gettempdir(), 'load_module')
    safe_mkdir(tmpdir)
    if tmpdir not in sys.path:
        sys.path.insert(0, tmpdir)
    mod_name = 'x' + uuid.uuid4().hex
    nfname = path.join(tmpdir, mod_name + '.py')
    shutil.copy(fname, nfname)
    mod = __import__(mod_name)
    safe_remove(nfname)
    return mod

未來規(guī)劃

  • 添加視頻整合能力
  • 添加 PPT(富文本轉圖片)整合能力
  • 沒了。。。

到了這里,關于【Python 自動化】自媒體剪輯第一版·思路簡述與技術方案的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉載,請注明出處: 如若內(nèi)容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • Python打開Windows可執(zhí)行性程序的幾種方法(Windows窗口自動化第一步)

    方法一:用Pywinauto第三包 方法二:使用os.system函數(shù)運行其他程序 方法三:win32api第三包使用ShellExecute函數(shù)運行其他程序 方法四:使用win32process.CreateProcess函數(shù) 方法七:用ctpyes模塊調用C函數(shù)

    2024年02月16日
    瀏覽(23)
  • 短視頻媒體處理系統(tǒng)應急響應自動化實踐

    短視頻媒體處理系統(tǒng)應急響應自動化實踐

    動手點關注 干貨不迷路 每天在世界各地都有海量用戶在短視頻 App 上分享充滿創(chuàng)意的視頻或是生活中的精彩故事。 由于使用者所在的環(huán)境不可控(高鐵、電梯等弱網(wǎng)環(huán)境),若直接播放原始畫質的視頻,可能導致觀看影片的過程中出現(xiàn)卡頓甚至無法播放的情形,導致觀影感

    2023年04月25日
    瀏覽(21)
  • python+pygame之飛機大戰(zhàn)-第一版源代碼分析

    目錄 一、源代碼 二、源代碼梳理分析: (一)代碼的主要結構 1、引用需要的模塊 2、定義的類 3、定義main()函數(shù):主程序 (二)代碼分析收獲 最好的學習方法是學習別人的代碼,加上自己以前比較喜歡雷電這款游戲,所以就從飛機大戰(zhàn)入手了,從網(wǎng)上下的源代碼和圖片素材

    2023年04月10日
    瀏覽(25)
  • 第一章 Web自動化入門

    第一章 Web自動化入門

    1、概念 由機器設備代替人工自動完成指定目標的過程 2、優(yōu)點 減少人工勞動力 提高工作效率 產(chǎn)品規(guī)格統(tǒng)一標準 規(guī)格化(批量生產(chǎn)) 概念:讓程序代替人工去驗證系統(tǒng)功能的過程 解決-回歸測試(重點) 解決-壓力測試 解決-兼容性測試(瀏覽器、分辨率、操作系統(tǒng)) 提高測

    2024年02月07日
    瀏覽(22)
  • 【iOS自動化測試】第一章:方案調研

    【iOS自動化測試】第一章:方案調研

    目前Android端已完成了相應的框架搭建,并實際落地產(chǎn)出了,由于Android使用的是Unittest+HtmlTestRunner產(chǎn)出報告,需要增加新功能的話需要改動到底層框架,所以目前在負責的iOS端打算采用Pytest+Allure方式來進行,優(yōu)點是更好的插件支持,報告也會更好看(裝逼)點 PS:Android端自動

    2024年02月09日
    瀏覽(28)
  • APP自動化第一步:Appium環(huán)境搭建

    APP自動化第一步:Appium環(huán)境搭建

    1.直接cmd窗口輸入pip install Appium-Python-Client 使用命令pip install selenium -U 首先進入網(wǎng)盤下載這三個軟件的壓縮包 2.雙擊進行安裝。 ? 3.點擊完成即可 三、安裝jdk第一步:檢查本機是否安裝過java 1、 查看自己當前電腦是否已安裝jdk點擊w7電腦左下角的開始按鈕,在出現(xiàn)的輸入框中

    2024年03月19日
    瀏覽(17)
  • 用Python編寫UI自動化用例第一步from selenium import webdriver,即導入第三方驅動包時報錯,高效解決辦法,報錯問題已解決

    用Python編寫UI自動化用例第一步from selenium import webdriver,即導入第三方驅動包時報錯,高效解決辦法,報錯問題已解決

    問題:在PyCharm中新建一個文件,py01_test_01.py文件。編寫UI自動化用例的第一步就是導入驅動(from selenium import webdriver),在已經(jīng)安裝selenium的情況下(pip install selenium==3.141.0),執(zhí)行的時候還是報錯。如下圖: ? ? 原因排查過程:從報錯信息,錯誤信息為沒有找到selenium。首先查

    2024年02月12日
    瀏覽(20)
  • web自動化之基礎內(nèi)容(全網(wǎng)最詳細,selenium環(huán)境準備和selenium工具操作)-第一天

    web自動化之基礎內(nèi)容(全網(wǎng)最詳細,selenium環(huán)境準備和selenium工具操作)-第一天

    (1)pycharm中要安裝selenium 也可用pip指令安裝pip install selenium (2)安裝chromedriver 根據(jù)你自己的chrome的版本,下載對應的chromedriver chrome版本在114前,下載的鏈接 http://chromedriver.storage.googleapis.com/index.html chrome版本超過114,下載的鏈接 Chrome for Testing availability 本人的chrome版本為

    2024年02月04日
    瀏覽(25)
  • (第一篇,踩坑無數(shù)得來的,對Ai自動化測試框架很有幫助)appium自動化測試時遇到不能使用element定位的在用坐標點擊之后獲取焦點如何輸入文本

    (第一篇,踩坑無數(shù)得來的,對Ai自動化測試框架很有幫助)appium自動化測試時遇到不能使用element定位的在用坐標點擊之后獲取焦點如何輸入文本

    ? 現(xiàn)在開發(fā)的前端界面使用vue或者更牛逼技術,導致使用appium或者uiautomator2做自動化測試時不能識別到元素,無法使用傳統(tǒng)的id,name或者xpath,這時我們需要使用坐標點擊文本框。有獲取坐標方法,下期寫一篇文章,可以在評論區(qū)提醒瑞克。 ?

    2024年02月03日
    瀏覽(21)
  • Python辦公自動化 – 自動化清理數(shù)據(jù)和自動化系統(tǒng)命令

    Python辦公自動化 – 自動化清理數(shù)據(jù)和自動化系統(tǒng)命令

    以下是往期的文章目錄,需要可以查看哦。 Python辦公自動化 – Excel和Word的操作運用 Python辦公自動化 – Python發(fā)送電子郵件和Outlook的集成 Python辦公自動化 – 對PDF文檔和PPT文檔的處理 Python辦公自動化 – 對Excel文檔和數(shù)據(jù)庫的操作運用、設置計劃任務 Python辦公自動化 – 對

    2024年02月01日
    瀏覽(26)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包