From: Carl Hetherington Date: Tue, 7 Feb 2012 12:12:01 +0000 (+0000) Subject: Move some hacks into the git repo. X-Git-Tag: v2.0.48~2415 X-Git-Url: https://main.carlh.net/gitweb/?a=commitdiff_plain;h=9ce56f6a2bdd076843a3b56d1cc27f3496b8528f;p=dcpomatic.git Move some hacks into the git repo. --- diff --git a/hacks/python-playback/config.py b/hacks/python-playback/config.py new file mode 100644 index 000000000..fecf261f5 --- /dev/null +++ b/hacks/python-playback/config.py @@ -0,0 +1,2 @@ + +LEFT_SCREEN_WIDTH = 1366 diff --git a/hacks/python-playback/dvdomatic b/hacks/python-playback/dvdomatic new file mode 100755 index 000000000..ce405f37e --- /dev/null +++ b/hacks/python-playback/dvdomatic @@ -0,0 +1,209 @@ +#!/usr/bin/python + +import os +import operator +import traceback +import pygtk +pygtk.require('2.0') +import gtk +import glib +import gobject +import film +import film_view +import player +import screens +import thumbs +import ratio +import util + +FILM_DIRECTORY = '/home/carl/DVD' + +current_player = None +films = [] +inhibit_selection_update = False + +def find_films(): + global films + films = [] + for root, dirs, files in os.walk(FILM_DIRECTORY): + for name in files: + if os.path.basename(name) == 'info': + films.append(film.Film(os.path.join(root, os.path.dirname(name)))) + + films.sort(key = operator.attrgetter('name')) + +def update_film_store(): + global film_store + global films + global inhibit_selection_update + inhibit_selection_update = True + film_store.clear() + for f in films: + film_store.append([f.name]) + inhibit_selection_update = False + +def update_screen_store(screen_store, screens): + screen_store.clear() + for s in screens.screens: + screen_store.append([s.name]) + +def create_film_tree_view(film_store): + view = gtk.TreeView(film_store) + column = gtk.TreeViewColumn() + view.append_column(column) + cell = gtk.CellRendererText() + column.pack_start(cell) + column.add_attribute(cell, 'text', 0) + view.get_selection().set_mode(gtk.SELECTION_SINGLE) + return view + +def create_screen_view(screen_store): + view = gtk.TreeView(screen_store) + column = gtk.TreeViewColumn() + view.append_column(column) + cell = gtk.CellRendererText() + column.pack_start(cell) + column.add_attribute(cell, 'text', 0) + view.get_selection().set_mode(gtk.SELECTION_SINGLE) + return view + +def get_selected_film(): + (model, iter) = film_tree_view.get_selection().get_selected() + + for f in films: + if f.name == model.get(iter, 0)[0]: + return f + + return None + +# @return Selected screen name +def get_selected_screen(): + (model, iter) = screen_view.get_selection().get_selected() + return model.get(iter, 0)[0] + +def film_selected(selection): + if inhibit_selection_update: + return + + film_view.set(get_selected_film()) + check_for_playability() + +def screen_selected(selection): + check_for_playability() + +def check_for_playability(): + f = get_selected_film() + if screens.get_format(get_selected_screen(), f.ratio) is not None: + play_button.set_label("Play") + play_button.set_sensitive(True) + else: + play_button.set_label("Cannot play: no setting for %s on screen %s" % (ratio.find(f.ratio).name(), get_selected_screen())) + play_button.set_sensitive(False) + +def update_status(s): + global current_player + if current_player is None: + s.set_text("Not playing") + return True + + position = current_player.time_pos + if position is None: + return True + position_hms = util.s_to_hms(position) + + length = current_player.length + if length is None: + return True + + remaining = length - position + remaining_hms = util.s_to_hms(remaining) + s.set_text("Playing: %d:%02d:%02d, %d:%02d:%02d remaining" % (position_hms[0], position_hms[1], position_hms[2], remaining_hms[0], remaining_hms[1], remaining_hms[2])) + return True + +def play_clicked(b): + global current_player + f = get_selected_film() + current_player = player.get_player(f, screens.get_format(get_selected_screen(), f.ratio)) + print current_player.args + +def stop_clicked(b): + global current_player + if current_player is not None: + current_player.stop() + current_player = None + +def add_film_clicked(b): + global films + c = gtk.FileChooserDialog("New Film", main_window, gtk.FILE_CHOOSER_ACTION_CREATE_FOLDER, (("Add", gtk.RESPONSE_OK))) + c.set_current_folder(FILM_DIRECTORY) + if c.run() == gtk.RESPONSE_OK: + f = film.Film() + f.data = c.get_filename() + f.name = os.path.basename(c.get_filename()) + f.write() + find_films() + update_film_store() + c.hide() + + for i in range(0, len(films)): + if films[i].name == f.name: + film_tree_view.get_selection().select_path((i, )) + +main_window = gtk.Window(gtk.WINDOW_TOPLEVEL) +main_window.set_title("DVD-o-matic") +main_window.maximize() + +main_hbox = gtk.HBox() +main_hbox.set_spacing(12) +main_hbox.set_border_width(12) +main_window.add(main_hbox) + +find_films() +film_view = film_view.FilmView(main_window) +screens = screens.Screens("screens") + +left_vbox = gtk.VBox() +left_vbox.set_spacing(12) +main_hbox.pack_start(left_vbox, False, False) +right_vbox = gtk.VBox() +right_vbox.set_spacing(12) +main_hbox.pack_start(right_vbox) + +film_store = gtk.ListStore(gobject.TYPE_STRING) +update_film_store() + +film_tree_view = create_film_tree_view(film_store) +left_vbox.pack_start(film_tree_view, True, True) +film_tree_view.get_selection().select_path((0, )) +film_tree_view.get_selection().connect("changed", film_selected) + +add_film_button = gtk.Button(stock = gtk.STOCK_ADD) +left_vbox.pack_start(add_film_button, False, False) +add_film_button.connect("clicked", add_film_clicked) + +screen_store = gtk.ListStore(gobject.TYPE_STRING) +update_screen_store(screen_store, screens) + +screen_view = create_screen_view(screen_store) +left_vbox.pack_start(screen_view, False, False) +screen_view.get_selection().select_path((0, )) +screen_view.get_selection().connect("changed", screen_selected) + +right_vbox.pack_start(film_view, False, False) +film_view.set(films[0]) + +play_button = gtk.Button("Play") +right_vbox.pack_start(play_button) +play_button.connect("clicked", play_clicked) + +stop_button = gtk.Button("Stop") +right_vbox.pack_start(stop_button) +stop_button.connect("clicked", stop_clicked) + +status = gtk.Label() +right_vbox.pack_start(status, False, False) +glib.timeout_add_seconds(1, update_status, status) + +check_for_playability() +main_window.show_all() +gtk.main() diff --git a/hacks/python-playback/film.py b/hacks/python-playback/film.py new file mode 100644 index 000000000..3ad128027 --- /dev/null +++ b/hacks/python-playback/film.py @@ -0,0 +1,188 @@ +import os +import subprocess +import shlex +import shutil +import player + +class Film: + def __init__(self, data = None): + # File or directory containing content + self.content = None + # True if content is in DVD format + self.dvd = False + # DVD title number + self.dvd_title = 1 + # Directory containing metadata + self.data = None + # Film name + self.name = None + # Number of pixels by which to crop the content from each edge + self.left_crop = 0 + self.top_crop = 0 + self.right_crop = 0 + self.bottom_crop = 0 + # Use deinterlacing filter + self.deinterlace = False + # Target ratio + self.ratio = 1.85 + # Audio stream ID to play + self.aid = None + + self.width = None + self.height = None + self.fps = None + self.length = None + + if data is not None: + self.data = data + f = open(os.path.join(self.data, 'info'), 'r') + while 1: + l = f.readline() + if l == '': + break + + d = l.strip() + + s = d.find(' ') + if s != -1: + key = d[:s] + value = d[s+1:] + + if key == 'name': + self.name = value + elif key == 'content': + self.content = value + elif key == 'dvd': + self.dvd = int(value) == 1 + elif key == 'dvd_title': + self.dvd_title = int(value) + elif key == 'left_crop': + self.left_crop = int(value) + elif key == 'top_crop': + self.top_crop = int(value) + elif key == 'right_crop': + self.right_crop = int(value) + elif key == 'bottom_crop': + self.bottom_crop = int(value) + elif key == 'deinterlace': + self.deinterlace = int(value) == 1 + elif key == 'ratio': + self.ratio = float(value) + elif key == 'aid': + self.aid = int(value) + elif key == 'width': + self.width = int(value) + elif key == 'height': + self.height = int(value) + elif key == 'fps': + self.fps = float(value) + elif key == 'length': + self.length = float(value) + + if self.width is None or self.height is None or self.fps is None or self.length is None: + self.update_content_metadata() + + def write(self): + try: + os.mkdir(self.data) + except OSError: + pass + + f = open(os.path.join(self.data, 'info'), 'w') + self.write_datum(f, 'name', self.name) + self.write_datum(f, 'content', self.content) + self.write_datum(f, 'dvd', int(self.dvd)) + self.write_datum(f, 'dvd_title', self.dvd_title) + self.write_datum(f, 'left_crop', self.left_crop) + self.write_datum(f, 'top_crop', self.top_crop) + self.write_datum(f, 'right_crop', self.right_crop) + self.write_datum(f, 'bottom_crop', self.bottom_crop) + self.write_datum(f, 'deinterlace', int(self.deinterlace)) + self.write_datum(f, 'ratio', self.ratio) + self.write_datum(f, 'aid', self.aid) + self.write_datum(f, 'width', self.width) + self.write_datum(f, 'height', self.height) + self.write_datum(f, 'fps', self.fps) + self.write_datum(f, 'length', self.length) + + def write_datum(self, f, key, value): + if value is not None: + print >>f,'%s %s' % (key, str(value)) + + def thumbs_dir(self): + t = os.path.join(self.data, 'thumbs') + + try: + os.mkdir(t) + except OSError: + pass + + return t + + def thumb(self, n): + return os.path.join(self.thumbs_dir(), str('%08d.png' % (n + 1))) + + def thumbs(self): + return len(os.listdir(self.thumbs_dir())) + + def remove_thumbs(self): + shutil.rmtree(self.thumbs_dir()) + + def make_thumbs(self): + num_thumbs = 128 + cl = self.player_command_line() + if self.length is not None: + sstep = self.length / num_thumbs + else: + sstep = 100 + cl.extra = '-vo png -frames %d -sstep %d -nosound' % (num_thumbs, sstep) + os.chdir(self.thumbs_dir()) + os.system(cl.get(True)) + + def set_dvd(self, d): + self.dvd = d + self.remove_thumbs() + + def set_dvd_title(self, t): + self.dvd_title = t + self.remove_thumbs() + + def set_content(self, c): + if c == self.content: + return + + self.content = c + self.update_content_metadata() + + def player_command_line(self): + cl = player.CommandLine() + cl.dvd = self.dvd + cl.dvd_title = self.dvd_title + cl.content = self.content + return cl + + def update_content_metadata(self): + if self.content is None: + return + + self.width = None + self.height = None + self.fps = None + self.length = None + + cl = self.player_command_line() + cl.extra = '-identify -vo null -ao null -frames 0' + text = subprocess.check_output(shlex.split(cl.get(True))).decode('utf-8') + lines = text.split('\n') + for l in lines: + s = l.strip() + b = s.split('=') + if len(b) == 2: + if b[0] == 'ID_VIDEO_WIDTH': + self.width = int(b[1]) + elif b[0] == 'ID_VIDEO_HEIGHT': + self.height = int(b[1]) + elif b[0] == 'ID_VIDEO_FPS': + self.fps = float(b[1]) + elif b[0] == 'ID_LENGTH': + self.length = float(b[1]) diff --git a/hacks/python-playback/film_view.py b/hacks/python-playback/film_view.py new file mode 100644 index 000000000..c11b2e657 --- /dev/null +++ b/hacks/python-playback/film_view.py @@ -0,0 +1,212 @@ +import os +import pygtk +pygtk.require('2.0') +import gtk +import ratio +import util +import thumbs + +class FilmView(gtk.HBox): + def __init__(self, parent): + gtk.HBox.__init__(self) + + self.parent_window = parent + + self.inhibit_save = True + + self.table = gtk.Table() + self.pack_start(self.table, True, True) + + self.table.set_row_spacings(4) + self.table.set_col_spacings(12) + self.film_name = gtk.Entry() + self.ratio = gtk.combo_box_new_text() + for r in ratio.ratios: + self.ratio.append_text(r.name()) + self.content_file_radio = gtk.RadioButton() + self.content_file_radio.set_label("File") + self.content_file_chooser = gtk.FileChooserDialog("Content", self.parent_window, gtk.FILE_CHOOSER_ACTION_OPEN, (("Select", gtk.RESPONSE_OK))) + self.content_file_button = gtk.FileChooserButton(self.content_file_chooser) + self.content_folder_radio = gtk.RadioButton() + self.content_folder_radio.set_label("Folder") + self.content_folder_radio.set_group(self.content_file_radio) + self.content_folder_chooser = gtk.FileChooserDialog("Content", self.parent_window, gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, (("Select", gtk.RESPONSE_OK))) + self.content_folder_button = gtk.FileChooserButton(self.content_folder_chooser) + self.dvd = gtk.CheckButton("DVD") + self.dvd_title = gtk.SpinButton() + self.dvd_title.set_range(0, 32) + self.dvd_title.set_increments(1, 4) + self.deinterlace = gtk.CheckButton("Deinterlace") + self.left_crop = self.crop_spinbutton() + self.right_crop = self.crop_spinbutton() + self.top_crop = self.crop_spinbutton() + self.bottom_crop = self.crop_spinbutton() + + # Information about the content (immutable) + self.source_size = self.label() + self.fps = self.label() + self.length = self.label() + + # Buttons + self.thumbs_button = gtk.Button("Show Thumbnails") + + self.film_name.connect("changed", self.changed, self) + self.ratio.connect("changed", self.changed, self) + self.deinterlace.connect("toggled", self.changed, self) + self.thumbs_button.connect("clicked", self.thumbs_clicked, self) + self.content_file_radio.connect("toggled", self.content_radio_toggled, self) + self.content_folder_radio.connect("toggled", self.content_radio_toggled, self) + self.content_file_button.connect("file-set", self.content_file_chooser_file_set, self) + self.content_folder_button.connect("file-set", self.content_folder_chooser_file_set, self) + self.dvd.connect("toggled", self.changed, self) + self.dvd_title.connect("value-changed", self.changed, self) + + n = 0 + self.table.attach(self.label("Film"), 0, 1, n, n + 1) + self.table.attach(self.film_name, 1, 2, n, n + 1) + n += 1 + self.table.attach(self.label("Ratio"), 0, 1, n, n + 1) + self.table.attach(self.ratio, 1, 2, n, n + 1) + n += 1 + self.table.attach(self.label("Content"), 0, 1, n, n + 1) + b = gtk.HBox() + b.set_spacing(4) + b.pack_start(self.content_file_radio, False, False) + b.pack_start(self.content_file_button, True, True) + b.pack_start(self.content_folder_radio, False, False) + b.pack_start(self.content_folder_button, True, True) + self.table.attach(b, 1, 2, n, n + 1) + n += 1 + self.table.attach(self.dvd, 0, 2, n, n + 1) + n += 1 + self.table.attach(self.label("DVD title"), 0, 1, n, n + 1) + self.table.attach(self.dvd_title, 1, 2, n, n + 1) + n += 1 + self.table.attach(self.deinterlace, 0, 2, n, n + 1) + n += 1 + self.table.attach(self.label("Left Crop"), 0, 1, n, n + 1) + self.table.attach(self.left_crop, 1, 2, n, n + 1) + n += 1 + self.table.attach(self.label("Right Crop"), 0, 1, n, n + 1) + self.table.attach(self.right_crop, 1, 2, n, n + 1) + n += 1 + self.table.attach(self.label("Top Crop"), 0, 1, n, n + 1) + self.table.attach(self.top_crop, 1, 2, n, n + 1) + n += 1 + self.table.attach(self.label("Bottom Crop"), 0, 1, n, n + 1) + self.table.attach(self.bottom_crop, 1, 2, n, n + 1) + n += 1 + self.table.attach(self.label("Source size"), 0, 1, n, n + 1) + self.table.attach(self.source_size, 1, 2, n, n + 1) + n += 1 + self.table.attach(self.label("Frames per second"), 0, 1, n, n + 1) + self.table.attach(self.fps, 1, 2, n, n + 1) + n += 1 + self.table.attach(self.label("Length"), 0, 1, n, n + 1) + self.table.attach(self.length, 1, 2, n, n + 1) + + self.right_vbox = gtk.VBox() + self.pack_start(self.right_vbox, False, False) + + self.right_vbox.pack_start(self.thumbs_button, False, False) + + self.inhibit_save = False + + def set(self, film): + self.inhibit_save = True + + self.film = film + self.film_name.set_text(film.name) + self.ratio.set_active(ratio.ratio_to_index(film.ratio)) + if film.content is not None: + if os.path.isfile(film.content): + self.set_content_is_file(True) + self.content_file_button.set_filename(film.content) + else: + self.set_content_is_file(False) + self.content_folder_button.set_filename(film.content) + self.dvd.set_active(film.dvd) + self.dvd_title.set_value(film.dvd_title) + self.dvd_title.set_sensitive(film.dvd) + self.deinterlace.set_active(film.deinterlace) + self.left_crop.set_value(film.left_crop) + self.right_crop.set_value(film.right_crop) + self.top_crop.set_value(film.top_crop) + self.bottom_crop.set_value(film.bottom_crop) + + # Content information + if film.width is not None and film.height is not None: + self.source_size.set_text("%dx%d" % (film.width, film.height)) + else: + self.source_size.set_text("Unknown") + if film.fps is not None: + self.fps.set_text(str(film.fps)) + if film.length is not None: + self.length.set_text("%d:%02d:%02d" % util.s_to_hms(film.length)) + + self.inhibit_save = False + + def set_content_is_file(self, f): + self.content_file_button.set_sensitive(f) + self.content_folder_button.set_sensitive(not f) + self.content_file_radio.set_active(f) + self.content_folder_radio.set_active(not f) + + def label(self, text = ""): + l = gtk.Label(text) + l.set_alignment(0, 0.5) + return l + + def changed(self, a, b): + self.dvd_title.set_sensitive(self.dvd.get_active()) + self.save_film() + + def crop_spinbutton(self): + s = gtk.SpinButton() + s.set_range(0, 1024) + s.set_increments(1, 16) + s.connect("value-changed", self.changed, self) + return s + + def save_film(self): + if self.inhibit_save: + return + + self.film.name = self.film_name.get_text() + self.film.ratio = ratio.index_to_ratio(self.ratio.get_active()).ratio + + if self.content_file_radio.get_active(): + self.film.set_content(self.content_file_button.get_filename()) + else: + self.film.set_content(self.content_folder_button.get_filename()) + self.film.set_dvd(self.dvd.get_active()) + self.film.set_dvd_title(self.dvd_title.get_value_as_int()) + self.film.deinterlace = self.deinterlace.get_active() + self.film.left_crop = self.left_crop.get_value_as_int() + self.film.right_crop = self.right_crop.get_value_as_int() + self.film.top_crop = self.top_crop.get_value_as_int() + self.film.bottom_crop = self.bottom_crop.get_value_as_int() + self.film.write() + + def thumbs_clicked(self, a, b): + if self.film.thumbs() == 0: + self.film.make_thumbs() + + t = thumbs.Thumbs(self.film) + t.run() + t.hide() + self.left_crop.set_value(t.left_crop()) + self.right_crop.set_value(t.right_crop()) + self.top_crop.set_value(t.top_crop()) + self.bottom_crop.set_value(t.bottom_crop()) + + def content_file_chooser_file_set(self, a, b): + self.changed(a, b) + + def content_folder_chooser_file_set(self, a, b): + self.changed(a, b) + + def content_radio_toggled(self, a, b): + self.set_content_is_file(self.content_file_radio.get_active()) + self.changed(a, b) + diff --git a/hacks/python-playback/player.py b/hacks/python-playback/player.py new file mode 100644 index 000000000..5cc8da711 --- /dev/null +++ b/hacks/python-playback/player.py @@ -0,0 +1,112 @@ +import os +import threading +import subprocess +import shlex +import select +import film +import config +import mplayer + +class CommandLine: + def __init__(self): + self.position_x = 0 + self.position_y = 0 + self.output_width = None + self.output_height = None + self.mov = False + self.delay = None + self.dvd = False + self.dvd_title = 1 + self.content = None + self.extra = '' + self.crop_x = None + self.crop_y = None + self.crop_w = None + self.crop_h = None + self.deinterlace = False + self.aid = None + + def get(self, with_binary): + # hqdn3d? + # nr, unsharp? + # -vo x11 appears to be necessary to prevent unwanted hardware scaling + # -noaspect stops mplayer rescaling to the movie's specified aspect ratio + args = '-vo x11 -noaspect -ao pulse -noborder -noautosub -nosub -sws 10 ' + args += '-geometry %d:%d ' % (self.position_x, self.position_y) + + # Video filters (passed to -vf) + + filters = [] + + if self.crop_x is not None or self.crop_y is not None or self.crop_w is not None or self.crop_h is not None: + crop = 'crop=' + if self.crop_w is not None and self.crop_h is not None: + crop += '%d:%d' % (self.crop_w, self.crop_h) + if self.crop_x is not None and self.crop_x is not None: + crop += ':%d:%d' % (self.crop_x, self.crop_y) + filters.append(crop) + + if self.output_width is not None or self.output_height is not None: + filters.append('scale=%d:%d' % (self.output_width, self.output_height)) + + # Post processing + pp = [] + if self.deinterlace: + pp.append('lb') + + # Deringing filter + pp.append('dr') + + if len(pp) > 0: + pp_string = 'pp=' + for i in range(0, len(pp)): + pp_string += pp[i] + if i < len(pp) - 1: + pp += ',' + + filters.append(pp_string) + + if len(filters) > 0: + args += '-vf ' + for i in range(0, len(filters)): + args += filters[i] + if i < len(filters) - 1: + args += ',' + args += ' ' + + if self.mov: + args += '-demuxer mov ' + if self.delay is not None: + args += '-delay %f ' % float(args.delay) + if self.aid is not None: + args += '-aid %s ' % self.aid + + args += self.extra + + if self.dvd: + data_specifier = 'dvd://%d -dvd-device \"%s\"' % (self.dvd_title, self.content) + else: + data_specifier = '\"%s\"' % self.content + + if with_binary: + return 'mplayer %s %s' % (args, data_specifier) + + return '%s %s' % (args, data_specifier) + +def get_player(film, format): + cl = CommandLine() + cl.dvd = film.dvd + cl.dvd_title = film.dvd_title + cl.content = film.content + cl.crop_w = film.width - film.left_crop - film.right_crop + cl.crop_h = film.height - film.top_crop - film.bottom_crop + cl.position_x = format.x + if format.external: + cl.position_x = format.x + config.LEFT_SCREEN_WIDTH + cl.position_y = format.y + cl.output_width = format.width + cl.output_height = format.height + cl.deinterlace = film.deinterlace + cl.aid = film.aid + return mplayer.Player(cl.get(False)) + diff --git a/hacks/python-playback/ratio.py b/hacks/python-playback/ratio.py new file mode 100644 index 000000000..62320dc8a --- /dev/null +++ b/hacks/python-playback/ratio.py @@ -0,0 +1,56 @@ +# Class to describe a Ratio, and a collection of common +# (and not-so-common) film ratios collected from Wikipedia. + +class Ratio: + def __init__(self, ratio, nickname = None): + self.nickname = nickname + self.ratio = ratio + + # @return presentation name of this ratio + def name(self): + if self.nickname is not None: + return "%.2f (%s)" % (self.ratio, self.nickname) + + return "%.2f" % self.ratio + +ratios = [] +ratios.append(Ratio(1.33, '4:3')) +ratios.append(Ratio(1.37, 'Academy')) +ratios.append(Ratio(1.78, '16:9')) +ratios.append(Ratio(1.85, 'Flat / widescreen')) +ratios.append(Ratio(2.39, 'CinemaScope / Panavision')) +ratios.append(Ratio(1.15, 'Movietone')) +ratios.append(Ratio(1.43, 'IMAX')) +ratios.append(Ratio(1.5)) +ratios.append(Ratio(1.56, '14:9')) +ratios.append(Ratio(1.6, '16:10')) +ratios.append(Ratio(1.67)) +ratios.append(Ratio(2, 'SuperScope')) +ratios.append(Ratio(2.2, 'Todd-AO')) +ratios.append(Ratio(2.35, 'Early CinemaScope / Panavision')) +ratios.append(Ratio(2.37, '21:9')) +ratios.append(Ratio(2.55, 'CinemaScope 55')) +ratios.append(Ratio(2.59, 'Cinerama')) +ratios.append(Ratio(2.76, 'Ultra Panavision')) +ratios.append(Ratio(2.93, 'MGM Camera 65')) +ratios.append(Ratio(4, 'Polyvision')) + +# Find a Ratio object from a fractional ratio +def find(ratio): + for r in ratios: + if r.ratio == ratio: + return r + + return None + +# @return the ith ratio +def index_to_ratio(i): + return ratios[i] + +# @return the index within the ratios list of a given fractional ratio +def ratio_to_index(r): + for i in range(0, len(ratios)): + if ratios[i].ratio == r: + return i + + return None diff --git a/hacks/python-playback/screens b/hacks/python-playback/screens new file mode 100644 index 000000000..f389cb1c4 --- /dev/null +++ b/hacks/python-playback/screens @@ -0,0 +1,62 @@ +# Screen 1 (untested) +screen 1 +ratio 1.85 +x 175 +y 100 +width 1550 +height 950 +external 1 +ratio 2.39 +x 0 +y 200 +width 2000 +height 860 +external 1 + +# Screen 2 +screen 2 +ratio 1.85 +x 175 +y 100 +width 1550 +height 950 +external 1 +ratio 2.39 +x 0 +y 200 +width 2000 +height 860 +external 1 + +# Screen 3 (untested) +screen 3 +ratio 1.85 +x 175 +y 100 +width 1550 +height 950 +external 1 +ratio 2.39 +x 0 +y 200 +width 2000 +height 860 +external 1 + +# Carl's Laptop +screen laptop +ratio 1.85 +x 0 +y 0 +width 1366 +height 738 +ratio 2.39 +x 0 +y 0 +width 1366 +height 572 +ratio 1.78 +x 0 +y 0 +width 1366 +height 767 diff --git a/hacks/python-playback/screens.py b/hacks/python-playback/screens.py new file mode 100644 index 000000000..4230a4cf8 --- /dev/null +++ b/hacks/python-playback/screens.py @@ -0,0 +1,85 @@ +#!/usr/bin/python + +class Screen: + def __init__(self): + self.name = None + self.formats = [] + +class Format: + def __init__(self): + self.ratio = None + self.x = None + self.y = None + self.width = None + self.height = None + self.external = False + +class Screens: + def __init__(self, file): + + self.screens = [] + + f = open(file, 'r') + current_screen = None + current_format = None + while 1: + l = f.readline() + if l == '': + break + if len(l) > 0 and l[0] == '#': + continue + + s = l.strip() + + if len(s) == 0: + continue + + b = s.split() + + if len(b) != 2: + print "WARNING: ignored line `%s' in screens file" % (s) + continue + + if b[0] == 'screen': + if current_format is not None: + current_screen.formats.append(current_format) + current_format = None + + if current_screen is not None: + self.screens.append(current_screen) + current_screen = None + + current_screen = Screen() + current_screen.name = b[1] + elif b[0] == 'ratio': + if current_format is not None: + current_screen.formats.append(current_format) + current_format = None + + current_format = Format() + current_format.ratio = float(b[1]) + elif b[0] == 'x': + current_format.x = int(b[1]) + elif b[0] == 'y': + current_format.y = int(b[1]) + elif b[0] == 'width': + current_format.width = int(b[1]) + elif b[0] == 'height': + current_format.height = int(b[1]) + elif b[0] == 'external': + current_format.external = int(b[1]) == 1 + + if current_format is not None: + current_screen.formats.append(current_format) + + if current_screen is not None: + self.screens.append(current_screen) + + def get_format(self, screen, ratio): + for s in self.screens: + if s.name == screen: + for f in s.formats: + if f.ratio == ratio: + return f + + return None diff --git a/hacks/python-playback/thumbs.py b/hacks/python-playback/thumbs.py new file mode 100644 index 000000000..921f82f5d --- /dev/null +++ b/hacks/python-playback/thumbs.py @@ -0,0 +1,76 @@ +# GUI to display thumbnails and allow cropping +# to be set up + +import os +import sys +import pygtk +pygtk.require('2.0') +import gtk +import film +import player + +class Thumbs(gtk.Dialog): + def __init__(self, film): + gtk.Dialog.__init__(self) + self.film = film + self.controls = gtk.Table() + self.controls.set_col_spacings(4) + self.thumb_adj = gtk.Adjustment(0, 0, self.film.thumbs() - 1, 1, 10) + self.add_control("Thumbnail", self.thumb_adj, 0) + self.left_crop_adj = gtk.Adjustment(self.film.left_crop, 0, 1024, 1, 128) + self.add_control("Left crop", self.left_crop_adj, 1) + self.right_crop_adj = gtk.Adjustment(self.film.right_crop, 0, 1024, 1, 128) + self.add_control("Right crop", self.right_crop_adj, 2) + self.top_crop_adj = gtk.Adjustment(self.film.top_crop, 0, 1024, 1, 128) + self.add_control("Top crop", self.top_crop_adj, 3) + self.bottom_crop_adj = gtk.Adjustment(self.film.bottom_crop, 0, 1024, 1, 128) + self.add_control("Bottom crop", self.bottom_crop_adj, 4) + self.display_image = gtk.Image() + self.update_display() + window_box = gtk.HBox() + window_box.set_spacing(12) + + controls_vbox = gtk.VBox() + controls_vbox.set_spacing(4) + controls_vbox.pack_start(self.controls, False, False) + + window_box.pack_start(controls_vbox, True, True) + window_box.pack_start(self.display_image) + + self.set_title("%s Thumbnails" % film.name) + self.get_content_area().add(window_box) + self.add_button("Close", gtk.RESPONSE_ACCEPT) + self.show_all() + + for a in [self.thumb_adj, self.left_crop_adj, self.right_crop_adj, self.top_crop_adj, self.bottom_crop_adj]: + a.connect('value-changed', self.update_display, self) + + def add_control(self, name, adj, n): + l = gtk.Label(name) + l.set_alignment(1, 0.5) + self.controls.attach(l, 0, 1, n, n + 1) + s = gtk.SpinButton(adj) + self.controls.attach(s, 1, 2, n, n + 1) + + def update_display(self, a = None, b = None): + thumb_pixbuf = gtk.gdk.pixbuf_new_from_file(self.film.thumb(self.thumb_adj.get_value())) + self.width = thumb_pixbuf.get_width() + self.height = thumb_pixbuf.get_height() + left = self.left_crop() + right = self.right_crop() + top = self.top_crop() + bottom = self.bottom_crop() + pixbuf = thumb_pixbuf.subpixbuf(left, top, self.width - left - right, self.height - top - bottom) + self.display_image.set_from_pixbuf(pixbuf) + + def top_crop(self): + return int(self.top_crop_adj.get_value()) + + def bottom_crop(self): + return int(self.bottom_crop_adj.get_value()) + + def left_crop(self): + return int(self.left_crop_adj.get_value()) + + def right_crop(self): + return int(self.right_crop_adj.get_value()) diff --git a/hacks/python-playback/util.py b/hacks/python-playback/util.py new file mode 100644 index 000000000..d78abdd00 --- /dev/null +++ b/hacks/python-playback/util.py @@ -0,0 +1,7 @@ + +def s_to_hms(s): + m = int(s / 60) + s -= (m * 60) + h = int(m / 60) + m -= (h * 60) + return (h, m, s) diff --git a/hacks/python-playback/xrandr-notes b/hacks/python-playback/xrandr-notes new file mode 100644 index 000000000..eeabf1423 --- /dev/null +++ b/hacks/python-playback/xrandr-notes @@ -0,0 +1,17 @@ +Recommended 1680 x 1050, 60 fps +xrandr --output HDMI1 --mode 0xbc + +List modes +xrandr --verbose -q + +2048 x 1024, 24 fps +xrandr --output HDMI1 --mode 0xd1 + +cvt +to give modeline, then +xrandr --newmode modeline +then add +xrandr --verbose --addmode HDMI1 modename +then activate +xrandr --output HDMI1 --mode foo +