Browse Source

Use listio for maps reading/writing. Use video-composer for video editing.

master
Jakub Valenta 4 years ago
parent
commit
bbae51a831
11 changed files with 50 additions and 277 deletions
  1. +2
    -1
      README.md
  2. +5
    -0
      tv-series-migrate
  3. +0
    -5
      tv-series-text
  4. +0
    -5
      tv-series-video
  5. +0
    -33
      tv_series/common.py
  6. +10
    -10
      tv_series/download_subs.py
  7. +3
    -4
      tv_series/find_episode_ids.py
  8. +9
    -9
      tv_series/search_subs.py
  9. +0
    -36
      tv_series/text.py
  10. +21
    -0
      tv_series/utils.py
  11. +0
    -174
      tv_series/video.py

+ 2
- 1
README.md View File

@@ -10,6 +10,7 @@ This software requires Python 2.7. See [Pythons's website](https://www.python.or
When you have Python 2.7 installed, install required packages with pip (Python's package management system):

```
pip2 install listio
pip2 install requests
pip2 install imdbpy
pip2 install "git+https://github.com/agonzalezro/python-opensubtitles#egg=python-opensubtitles"
@@ -54,7 +55,7 @@ True Detective
Then call:

```
tv-series-find-episode-ids.py -i my_series.txt -o my_episode_ids.txt
tv-series-find-episode-ids -i my_series.txt -o my_episode_ids.txt
```

Episode IDs for all the TV series mentioned in `my_series.txt` will be written to `my_episode_ids.txt` like this:


+ 5
- 0
tv-series-migrate View File

@@ -0,0 +1,5 @@
#!/usr/bin/env python

import tv_series.utils
if __name__ == '__main__':
tv_series.utils.migrate_custom_map_to_csv()

+ 0
- 5
tv-series-text View File

@@ -1,5 +0,0 @@
#!/usr/bin/env python

import tv_series.text
if __name__ == '__main__':
tv_series.text.main()

+ 0
- 5
tv-series-video View File

@@ -1,5 +0,0 @@
#!/usr/bin/env python

import tv_series.video
if __name__ == '__main__':
tv_series.video.create_super_cut()

+ 0
- 33
tv_series/common.py View File

@@ -1,33 +0,0 @@
SEP = ' :: '


def read_list_from_file(file_path, first_column_only=True):
items = []
try:
with open(file_path, 'r') as f:
for line in f:
line = line.strip()
if not line or line[0] == '#':
continue
if SEP not in line:
items.append(line)
continue
line_columns = line.split(SEP)
if first_column_only:
items.append(line_columns[0])
else:
items.append(line_columns)
except FileNotFoundError:
pass
return items


def write_list_to_file(file_path, items):
with open(file_path, 'a') as f:
for item in items:
if type(item) == tuple or type(item) == list:
f.write(SEP.join([str(i) for i in item]))
else:
f.write(item)
f.write('\n')
f.flush()

+ 10
- 10
tv_series/download_subs.py View File

@@ -19,15 +19,14 @@ try:
except ImportError:
pass

import listio
import requests
from pythonopensubtitles.opensubtitles import OpenSubtitles

from tv_series import common


DEFAULT_TIMEOUT = 15
FILE_NAME_DOWNLOADED = 'episode_ids_downloaded.txt'
FILE_NAME_ERROR = 'episode_ids_failed.txt'
FILE_NAME_DOWNLOADED = 'episode_ids_downloaded.csv'
FILE_NAME_ERROR = 'episode_ids_failed.csv'


def _gzip_decompress(s):
@@ -107,17 +106,18 @@ def download_subs_and_cache_results():
opensub = OpenSubtitles()
opensub.login(os.environ['OPENSUB_USER'], os.environ['OPENSUB_PASSWD'])

ids = common.read_list_from_file(args.inputfile)
ids = listio.read_list(args.inputfile)
if args.shuffle:
random.shuffle(ids)

# Need to call list to make the list concatenation work in Python 2.
excl = list(itertools.chain.from_iterable(
[common.read_list_from_file(os.path.join(args.cachedir, f))
for f in (FILE_NAME_DOWNLOADED, FILE_NAME_ERROR)]
))
excl = []
for f in (FILE_NAME_DOWNLOADED, FILE_NAME_ERROR):
lines = listio.read_map(os.path.join(args.cachedir, f))
for line in lines:
excl.append(line[0])

common.write_list_to_file(
listio.write_list(
os.path.join(args.cachedir, FILE_NAME_DOWNLOADED),
download_subs(
ids,


+ 3
- 4
tv_series/find_episode_ids.py View File

@@ -1,10 +1,9 @@
import sys

import listio
from imdb import IMDb
from imdb import IMDbDataAccessError

from tv_series import common


def find_episode_ids(series_titles, ia):
for series_title in series_titles:
@@ -62,10 +61,10 @@ def find_and_write_episode_ids():
' file will be added')
args = parser.parse_args()

common.write_list_to_file(
listio.write_list(
args.outputfile,
find_episode_ids(
common.read_list_from_file(args.inputfile),
listio.read_list(args.inputfile),
IMDb()
)
)


+ 9
- 9
tv_series/search_subs.py View File

@@ -4,11 +4,10 @@ import re
import readline
import sys

import listio
import pysrt
from termcolor import colored

from tv_series import common


def parse_sub_text(s):
return s.replace('\n', ' ')
@@ -85,9 +84,10 @@ def iter_subs(dir_path):
continue
if not entry.is_file() or not entry.name.endswith('.srt'):
continue
abspath = os.path.abspath(entry.path)
subs = read_subs(entry.path)
if subs is not None:
yield (entry.path, subs)
yield (abspath, subs)


def search_subs(subs_dir, excl, regex_list):
@@ -224,17 +224,17 @@ def search_and_approve_subs():

excl = [
convert_answer_to_match(answer)
for answer in common.read_list_from_file(args.outputfile, False)
for answer in listio.read_map(args.outputfile)
]
regex_list = [
compile_regex(pattern)
for pattern in common.read_list_from_file(args.patterns)
for pattern in listio.read_list(args.patterns)
]
subs_dir = iter_subs(args.inputdir)
matches_with_context = search_subs(subs_dir, excl, regex_list)
answers = approve_matches(matches_with_context)

common.write_list_to_file(args.outputfile, answers)
listio.write_map(args.outputfile, answers)

sys.exit()

@@ -252,13 +252,13 @@ def check_approved_subs():
' stored')
args = parser.parse_args()

answers = common.read_list_from_file(args.inputfile, False)
answers = listio.read_map(args.inputfile)
approved = filter_approved_answers(answers)
matches = [convert_answer_to_match(answer) for answer in approved]
matches_with_context = add_subs_context_to_matches(matches, 3)
new_answers = approve_matches(matches_with_context)

common.write_list_to_file(args.outputfile, new_answers)
listio.write_map(args.outputfile, new_answers)

sys.exit()

@@ -273,7 +273,7 @@ def print_approved_subs():
help='path to a file with the answers')
args = parser.parse_args()

answers = common.read_list_from_file(args.inputfile, False)
answers = listio.read_map(args.inputfile)
approved = filter_approved_answers(answers)
matches = [convert_answer_to_match(answer) for answer in approved]
matches_with_context = add_subs_context_to_matches(matches, 3)


+ 0
- 36
tv_series/text.py View File

@@ -1,36 +0,0 @@
#!/usr/bin/env python

from moviepy.editor import *

from tv_series import common


SIZE = (1280, 720)
DURATION = 3
FPS = 24


def main():
import argparse

parser = argparse.ArgumentParser(
description='TV Series Tools: Text'
)
parser.add_argument('--input', '-i', dest='inputfile', required=True,
help='input file')
args = parser.parse_args()

inp = common.read_list_from_file(args.inputfile, False)

for file_path, text in inp:
text_clip = TextClip(text, color='white', font="Arial", fontsize=48)
composite_clip = CompositeVideoClip(
[text_clip.set_pos('center')],
size=SIZE
)
final_clip = composite_clip.subclip(0, DURATION)
final_clip.write_videofile(file_path, fps=FPS, codec='png')


if __name__ == '__main__':
main()

+ 21
- 0
tv_series/utils.py View File

@@ -0,0 +1,21 @@
import listio


def migrate_custom_map_to_csv():
import argparse

parser = argparse.ArgumentParser(
description='TV Series Tools: Migrate custom map to CSV'
)
parser.add_argument('--input', '-i', dest='inputfile', required=True,
help='input custom map file path')
parser.add_argument('--output', '-o', dest='outputfile', required=True,
help='output csv file path')
args = parser.parse_args()

listio.write_map(
args.outputfile,
[line.split(' :: ') for line in listio.read_lines(args.inputfile)]
)

sys.exit()

+ 0
- 174
tv_series/video.py View File

@@ -1,174 +0,0 @@
import math
import os
import sys

from moviepy.editor import *
from moviepy.video.tools.subtitles import SubtitlesClip

from tv_series import common


DEFAULT_LIMIT = -1
DEFAULT_FPS = 24
DEFAULT_EXT = '.avi'
DEFAULT_DIR = 'clips'

VIDEO_FORMAT_SIZE_W = 640
VIDEO_FORMAT_SIZE_H = 360

DEBUG_SKIP = ()


def render(clip, file_path, fps=None, ext=None):
if fps is None:
fps = DEFAULT_FPS
if ext is None:
ext = DEFAULT_EXT
base, _ = os.path.splitext(file_path)
if ext == '.avi':
codec = 'png'
clip.write_videofile(base + ext, fps=fps, codec=codec)


def subtitle_generator(txt):
return TextClip(txt, font='Georgia-Regular', fontsize=24, color='white')


def parse_duration(duration):
return duration.replace(',', '.')


def format_duration(duration):
return duration.replace(':', '_').replace('.', '_')


def format_clip_file_path(file_path, cut_start, cut_end):
file_dir, file_basename = os.path.split(file_path)
file_name, file_ext = os.path.splitext(file_basename)
new_path_without_ext = os.path.join(file_dir, DEFAULT_DIR, file_name)
return '{path}-{start}-{end}{ext}'.format(
path=new_path_without_ext,
start=format_duration(cut_start),
end=format_duration(cut_end),
ext=file_ext
)


def create_super_cut():
import argparse

parser = argparse.ArgumentParser(
description='TV Series Tools: Video'
)
parser.add_argument('--input', '-i', dest='inputfile', required=True,
help='file path to a file containing info on how to'
' cut the clips')
parser.add_argument('--output', '-o', dest='outputfile', required=True,
help='output video file path')
parser.add_argument('--clips', '-c', dest='clipsdir', required=True,
help='clips video files location')
parser.add_argument('--join', '-j', dest='join', action='store_true',
help='concat cut video clips')
parser.add_argument('--change-fps', '-f', dest='change_fps', type=int,
default=DEFAULT_FPS,
help='video fps')
parser.add_argument('--resize-width', '-rw', dest='resize_width',
type=int,
help='resize width; you must set both --resize-width'
' and --resize-height')
parser.add_argument('--resize-height', '-rh', dest='resize_height',
type=int,
help='resize height; you must set both --resize-width'
' and --resize-height')
parser.add_argument('--limit', '-l', dest='limit', type=int,
default=DEFAULT_LIMIT,
help='process only first <limit> clips')
parser.add_argument('--speed', '-s', dest='speed', type=float,
help='speed of the composition; the standard speed'
' will be multiplied by this number, hence'
' 1 = normal speed, 0.5 = half the normal speed,'
' 3 = three times as fast, etc.')
args = parser.parse_args()

inp = common.read_list_from_file(args.inputfile, False)
if not inp:
sys.exit(1)

video_clips = {}
all_clips = []

i = 0
for file_name, start, end in inp:
if i == args.limit:
break
i = i + 1

file_path = os.path.join(args.clipsdir, file_name)
subtitles_path = os.path.splitext(file_path)[0] + '.srt'
cut_start = parse_duration(start)
cut_end = parse_duration(end)
print(
'CLIP {}\n'
' {}\n'
' {}\n'
' {} --> {}'
.format(
i,
file_path,
subtitles_path,
cut_start,
cut_end
)
)
if file_name in DEBUG_SKIP:
print(' SKIP')
continue
if not args.join:
clip_file_path = format_clip_file_path(
file_path, cut_start, cut_end)
if os.path.isfile(clip_file_path):
print(' SKIP EXISTS')
continue
if not os.path.isfile(file_path):
print(' SKIP FILE NOT FOUND')
continue

if file_path not in video_clips:
video_clips[file_path] = VideoFileClip(file_path)
video_clip = video_clips[file_path]
video_sub_clip = video_clip.subclip(
cut_start,
cut_end
)
if args.change_fps:
video_sub_clip = video_sub_clip.set_fps(args.change_fps)
if args.resize_width and args.resize_height:
video_sub_clip = video_sub_clip.resize(
width=args.resize_width, height=args.resize_height
)
# subtitles_clip = SubtitlesClip(
# subtitles_path,
# subtitle_generator
# )
composite_clip = video_sub_clip
# composite_clip = CompositeVideoClip([video_sub_clip, subtitles_clip])
# composite_clips.append(composite_clip)

if args.speed is not None:
# composite_clip = composite_clip.speedx(factor=args.speed)
pass

if args.join:
all_clips.append(composite_clip)
else:
render(composite_clip, clip_file_path, fps=args.change_fps)

if args.join:
joined_clip = concatenate_videoclips(all_clips)
render(joined_clip, args.outputfile, fps=args.change_fps)

sys.exit()


if __name__ == '__main__':
create_super_cut()

Loading…
Cancel
Save