mediainfogui 1.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ include mediainfogui/mediainfo.png
2
+ include README.md
@@ -0,0 +1,46 @@
1
+ Metadata-Version: 2.4
2
+ Name: mediainfogui
3
+ Version: 1.3.0
4
+ Summary: GUI for MediaInfo data - Qt6, GTK3 and GTK4
5
+ License-Expression: GPL-2.0-or-later
6
+ Project-URL: Homepage, https://github.com/kanehekili/MediaInfoGui
7
+ Requires-Python: >=3.9
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: PyQt6
10
+
11
+ # MediaInfoGui
12
+ GUI for MediaInfo data — available in Qt6, GTK3 and GTK4
13
+
14
+ Version 1.3.0
15
+
16
+ This simple GUI for the media info binary runs on Linux. Provides a fast & clean overview of the contents of a media file.
17
+
18
+ ##### Qt6 Implementation
19
+
20
+ ![Screenshot](./Screenshot-Qt5.png)
21
+
22
+ ##### GTK3 Implementation
23
+
24
+ ![Screenshot](./Screenshot-GTK3.png)
25
+
26
+ ### Features
27
+ - Opens on a given filename, usually from a file manager
28
+ - Shows codec data for video, audio and images
29
+ - Displays full MPEG-TS program structure (PAT/PMT), including broken or unresolvable programs — requires `ffprobe`
30
+ - Section headers highlighted with the theme's selection colour
31
+ - Copy to clipboard button ("Clip") for pasting media info into other applications
32
+ - Supports light and dark themes
33
+
34
+ ### Prerequisites
35
+ - `mediainfo`
36
+ - Qt6: `python3-pyqt6` (PyQt6)
37
+ - GTK3: `python3-gi` with GTK 3
38
+ - GTK4: `python3-gi` with GTK 4
39
+ - Optional: `ffprobe` (from ffmpeg) for MPEG-TS program info
40
+
41
+ ### How to install
42
+ * Download the MediaInfoGui*.tar contained in the "build" folder
43
+ * Unpack it and run the command **sudo ./install.sh** in the unpacked folder.
44
+ * Select 1 for Qt6, 2 for GTK3, or 3 for GTK4
45
+ * Install just copies a desktop file and some python scripts to /usr/local/sbin/MediaInfoGui
46
+
@@ -0,0 +1,36 @@
1
+ # MediaInfoGui
2
+ GUI for MediaInfo data — available in Qt6, GTK3 and GTK4
3
+
4
+ Version 1.3.0
5
+
6
+ This simple GUI for the media info binary runs on Linux. Provides a fast & clean overview of the contents of a media file.
7
+
8
+ ##### Qt6 Implementation
9
+
10
+ ![Screenshot](./Screenshot-Qt5.png)
11
+
12
+ ##### GTK3 Implementation
13
+
14
+ ![Screenshot](./Screenshot-GTK3.png)
15
+
16
+ ### Features
17
+ - Opens on a given filename, usually from a file manager
18
+ - Shows codec data for video, audio and images
19
+ - Displays full MPEG-TS program structure (PAT/PMT), including broken or unresolvable programs — requires `ffprobe`
20
+ - Section headers highlighted with the theme's selection colour
21
+ - Copy to clipboard button ("Clip") for pasting media info into other applications
22
+ - Supports light and dark themes
23
+
24
+ ### Prerequisites
25
+ - `mediainfo`
26
+ - Qt6: `python3-pyqt6` (PyQt6)
27
+ - GTK3: `python3-gi` with GTK 3
28
+ - GTK4: `python3-gi` with GTK 4
29
+ - Optional: `ffprobe` (from ffmpeg) for MPEG-TS program info
30
+
31
+ ### How to install
32
+ * Download the MediaInfoGui*.tar contained in the "build" folder
33
+ * Unpack it and run the command **sudo ./install.sh** in the unpacked folder.
34
+ * Select 1 for Qt6, 2 for GTK3, or 3 for GTK4
35
+ * Install just copies a desktop file and some python scripts to /usr/local/sbin/MediaInfoGui
36
+
@@ -0,0 +1,185 @@
1
+ # -*- coding: iso-8859-15 -*-
2
+ '''
3
+ Created on Nov 25, 2011
4
+ Vern�nftige GTK Oberfl�che f�r media info
5
+ @author: kanehekili
6
+ '''
7
+ import subprocess
8
+ import sys
9
+ import json
10
+ import re
11
+ from subprocess import Popen
12
+
13
+
14
+ VERSION="1.3.0"
15
+
16
+
17
+ def parseLines(lines):
18
+ result = []
19
+ for line in lines:
20
+ row = line.decode("utf-8")
21
+ token = re.split('[ ]+:[ ]+', row)
22
+ result.append((token[0], token[1] if len(token) > 1 else ""))
23
+ return result
24
+
25
+
26
+ def isHeader(row):
27
+ return len(row[1]) == 0 and len(row[0]) > 0
28
+
29
+
30
+ def formatForClipboard(rows):
31
+ lines = []
32
+ for t0, t1 in rows:
33
+ if t0 and not t1:
34
+ lines.append(t0)
35
+ elif t0 or t1:
36
+ lines.append(f"{t0:<40} : {t1}")
37
+ else:
38
+ lines.append("")
39
+ return "\n".join(lines)
40
+
41
+
42
+ def _isMpegTS(lines):
43
+ for line in lines:
44
+ if b"MPEG-TS" in line:
45
+ return True
46
+ return False
47
+
48
+
49
+ def _streamResolvable(s):
50
+ ct = s.get("codec_type", "unknown")
51
+ if ct == "video":
52
+ return int(s.get("width", 0) or 0) > 0
53
+ if ct == "audio":
54
+ sr = s.get("sample_rate")
55
+ return sr is not None and str(sr) not in ("0", "N/A", "")
56
+ return False
57
+
58
+
59
+ def _getTSProgramLines(filename):
60
+ try:
61
+ proc = Popen(
62
+ ["ffprobe", "-v", "quiet", "-show_programs", "-of", "json", filename],
63
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE
64
+ )
65
+ out, _ = proc.communicate()
66
+ except FileNotFoundError:
67
+ return []
68
+
69
+ try:
70
+ data = json.loads(out)
71
+ except ValueError:
72
+ return []
73
+
74
+ programs = data.get("programs", [])
75
+ if not programs:
76
+ return []
77
+
78
+ lines = []
79
+ lines.append(b"MPEG-TS Programs")
80
+ lines.append(f"{'Program count':<40} : {len(programs)}".encode())
81
+
82
+ for prog in programs:
83
+ prog_id = prog.get("program_id", "?")
84
+ pmt_pid = prog.get("pmt_pid", "?")
85
+ streams = prog.get("streams", [])
86
+ nb = len(streams)
87
+
88
+ if nb == 0:
89
+ value = f"PMT PID {pmt_pid} - no streams (broken)"
90
+ else:
91
+ type_counts = {}
92
+ for s in streams:
93
+ ct = s.get("codec_type", "unknown")
94
+ type_counts[ct] = type_counts.get(ct, 0) + 1
95
+ type_str = ", ".join(f"{v} {k}" for k, v in type_counts.items())
96
+ resolvable = any(_streamResolvable(s) for s in streams)
97
+ broken = "" if resolvable else " (unresolvable)"
98
+ value = f"PMT PID {pmt_pid} - {nb} stream{'s' if nb != 1 else ''} ({type_str}){broken}"
99
+
100
+ lines.append(f"{'Program ' + str(prog_id):<40} : {value}".encode())
101
+
102
+ lines.append(b"")
103
+ return lines
104
+
105
+
106
+ def _insertAfterGeneral(lines, extraLines):
107
+ in_general = False
108
+ for i, line in enumerate(lines):
109
+ row = line.decode("utf-8", errors="replace")
110
+ is_section_header = len(re.split(r'[ ]+:[ ]+', row)) == 1
111
+ stripped = row.strip()
112
+
113
+ if is_section_header and stripped == "General":
114
+ in_general = True
115
+ continue
116
+ if in_general and is_section_header:
117
+ if stripped == "":
118
+ # insert after this blank separator line
119
+ return lines[:i+1] + extraLines + lines[i+1:]
120
+ else:
121
+ # no blank line found before next section - insert one
122
+ return lines[:i] + [b""] + extraLines + lines[i:]
123
+
124
+ return lines + [b""] + extraLines
125
+
126
+
127
+ def readMediaInfo(type,filename):
128
+ nameValid=False
129
+ if len(filename)>3:
130
+ result=Popen(["mediainfo",filename],stdout=subprocess.PIPE).communicate()[0]
131
+ nameValid= len(result) > 10
132
+
133
+ if not nameValid:
134
+ if type == "gtk3":
135
+ from . import MediaInfoWidgetsGTK3
136
+ MediaInfoWidgetsGTK3.showMessage("Invalid File for Media Info")
137
+ elif type == "gtk4":
138
+ from . import MediaInfoWidgetsGTK4
139
+ MediaInfoWidgetsGTK4.showMessage("Invalid File for Media Info")
140
+ else:
141
+ from . import MediaInfoWidgetsQt
142
+ MediaInfoWidgetsQt.showMessage("Invalid File for Media Info")
143
+ return 0
144
+
145
+ lines = result.splitlines()
146
+ if _isMpegTS(lines):
147
+ tsLines = _getTSProgramLines(filename)
148
+ if tsLines:
149
+ lines = _insertAfterGeneral(lines, tsLines)
150
+
151
+ showListDialog(type,filename,lines)
152
+
153
+
154
+ def showListDialog(type,fileName,mediaInfoList):
155
+ paths = fileName.split("/")
156
+ pLen = len(paths)
157
+ item = paths[pLen-2]+"/"+paths[pLen-1]
158
+ rows = parseLines(mediaInfoList)
159
+ if type == "gtk3":
160
+ from . import MediaInfoWidgetsGTK3
161
+ MediaInfoWidgetsGTK3.main([item,rows])
162
+ elif type == "gtk4":
163
+ from . import MediaInfoWidgetsGTK4
164
+ MediaInfoWidgetsGTK4.main([item,rows])
165
+ else:
166
+ from . import MediaInfoWidgetsQt
167
+ MediaInfoWidgetsQt.main([item,rows])
168
+
169
+
170
+ def main(argv = None):
171
+ filename=""
172
+ type=""
173
+ if argv is None:
174
+ argv = sys.argv
175
+ if len(argv)>1:
176
+ type=argv[1]
177
+ if len(argv)>2:
178
+ filename=argv[2]
179
+
180
+
181
+ print("Version:"+VERSION)
182
+ readMediaInfo(type,filename)
183
+
184
+ if __name__ == '__main__':
185
+ sys.exit(main())
@@ -0,0 +1,171 @@
1
+ '''
2
+ Created on Nov 18, 2011
3
+
4
+ @author: kanehekili
5
+ '''
6
+ import sys
7
+ import os
8
+ import gi
9
+ import locale
10
+ gi.require_version('Gtk', '3.0')
11
+ from gi.repository import Gtk, Gdk,GLib,Pango
12
+ from .MediaInfoGui import isHeader, formatForClipboard
13
+
14
+ class MediaInfoView:
15
+
16
+ def __init__(self,fileName):
17
+ #create Window
18
+ self.window = Gtk.Window()
19
+ #set the window Title
20
+ self.window.set_title(fileName)
21
+ #the icon
22
+ homeDir = os.path.dirname(__file__)
23
+ appIcon = os.path.join(homeDir,"mediainfo.png")
24
+ self.window.set_icon_from_file(appIcon)
25
+ self.window.connect("delete_event",self.delete_event)
26
+ self.window.set_border_width(5)
27
+ self.window.set_size_request(500,600)
28
+ #create some kind of layout in which the widgets are packed
29
+ #layoutTable = gtk.Table(rows=2,columns=2,True)
30
+ layoutTable = Gtk.Table(rows=2,columns=2,homogeneous=False)
31
+ #TODO use buttonbox for buttons....
32
+
33
+ #create a widget the could contain the data
34
+ infoFrame=Gtk.Frame()
35
+ infoFrame.set_label("Media Info")
36
+ infoFrame.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
37
+
38
+
39
+ sw = self.createTextWidget()
40
+ sw.set_border_width(5)
41
+ vbox = Gtk.VBox(homogeneous=False, spacing=0)
42
+ vbox.pack_start(sw, expand=True, fill=True, padding=0)
43
+ hbox = Gtk.HBox(homogeneous=False, spacing=0)
44
+ hbox.pack_start(vbox, expand=True, fill=True, padding=1)
45
+
46
+ infoFrame.add(hbox)
47
+
48
+ layoutTable.attach(infoFrame,0,2,0,1,Gtk.AttachOptions.FILL| Gtk.AttachOptions.EXPAND,
49
+ Gtk.AttachOptions.FILL | Gtk.AttachOptions.EXPAND, 1, 1)
50
+
51
+
52
+ #create the button bar
53
+ btnBar = Gtk.HBox(homogeneous=False, spacing=0)
54
+ buttonCopy = Gtk.Button(label="Clip")
55
+ buttonCopy.connect("clicked", self.callback_copy)
56
+ buttonOK = Gtk.Button(label="egal", stock=Gtk.STOCK_OK)
57
+ buttonOK.connect("clicked", self.callback_btn_ok, "OK button")
58
+ btnBar.pack_start(buttonCopy, False, False, 0)
59
+ btnBar.pack_end(buttonOK, False, False, 0)
60
+ layoutTable.attach(btnBar, 0, 2, 1, 2,
61
+ Gtk.AttachOptions.FILL | Gtk.AttachOptions.EXPAND,
62
+ Gtk.AttachOptions.FILL, 1, 1)
63
+ #layoutTable.set_row_spacing(row=0,spacing=5)
64
+ self.window.add(layoutTable)
65
+
66
+ self.window.show_all()
67
+ sc = self.treeView.get_style_context()
68
+ self.header_bg_rgba = sc.get_background_color(Gtk.StateFlags.SELECTED)
69
+ self.header_fg_rgba = sc.get_color(Gtk.StateFlags.SELECTED)
70
+
71
+ ##create a text view
72
+ def createTextWidget(self):
73
+ self.treeView = Gtk.TreeView(model=self.createTreeStore())
74
+ self.treeView.set_grid_lines(Gtk.TreeViewGridLines.BOTH)
75
+ sel = self.treeView.get_selection()
76
+ sel.set_mode(Gtk.SelectionMode.NONE)
77
+
78
+ #fontName="Monospace 9"
79
+ fontName=""
80
+ pangoFont = Pango.FontDescription(fontName)
81
+ self.treeView.modify_font(pangoFont)
82
+ self.createColumn(self.treeView, "Item",0)
83
+ self.createColumn(self.treeView, "Data",1)
84
+
85
+ sw = Gtk.ScrolledWindow()
86
+ #sw.set_policy(Gtk.POLICY_AUTOMATIC, Gtk.POLICY_AUTOMATIC)
87
+ sw.add(self.treeView)
88
+ return sw
89
+
90
+ def createTreeStore(self):
91
+ self.store = Gtk.ListStore(str,str)
92
+ return self.store
93
+
94
+ def _headerCellData(self, column, renderer, model, iter, col_idx):
95
+ is_header = isHeader((model.get_value(iter, 0), model.get_value(iter, 1)))
96
+ if is_header:
97
+ renderer.set_property('cell-background-rgba', self.header_bg_rgba)
98
+ renderer.set_property('cell-background-set', True)
99
+ renderer.set_property('foreground-rgba', self.header_fg_rgba)
100
+ renderer.set_property('foreground-set', True)
101
+ renderer.set_property('weight', 700)
102
+ else:
103
+ renderer.set_property('cell-background-set', False)
104
+ renderer.set_property('foreground-set', False)
105
+ renderer.set_property('weight', 400)
106
+
107
+ def createColumn(self,columnContainer,headerString,columnID):
108
+ rendererText = Gtk.CellRendererText()
109
+ column = Gtk.TreeViewColumn(headerString, rendererText, text=columnID)
110
+ column.set_cell_data_func(rendererText, self._headerCellData, columnID)
111
+ columnContainer.append_column(column)
112
+
113
+ def fillTable(self, rows):
114
+ for row in rows:
115
+ self.addTextLine(row)
116
+
117
+ #adds an array of strings. For the media view we need the Item name and value
118
+ def addTextLine(self, strings):
119
+ self.store.append(strings)
120
+
121
+ def openView(self):
122
+ Gtk.main()
123
+ #control returns if quit is called
124
+ return 0;
125
+
126
+
127
+ # ------------ Callback section -----------------
128
+ def callback_copy(self, widget, data=None):
129
+ clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
130
+ clipboard.set_text(formatForClipboard([(r[0], r[1]) for r in self.store]), -1)
131
+ clipboard.store()
132
+
133
+ def callback_btn_ok(self, widget, data=None):
134
+ Gtk.main_quit();
135
+
136
+ # This callback quits the program
137
+ def delete_event(self, widget, event, data=None):
138
+ Gtk.main_quit()
139
+ return False
140
+
141
+
142
+ def showMessage(messageString):
143
+ message = Gtk.MessageDialog(None,
144
+ Gtk.DialogFlags.MODAL,
145
+ Gtk.MessageType.INFO,
146
+ Gtk.ButtonsType.NONE,
147
+ messageString)
148
+ message.add_button(Gtk.STOCK_QUIT, Gtk.ResponseType.CLOSE)
149
+ resp = message.run()
150
+ closewidget(message)
151
+
152
+ #hook to ensure closing widget
153
+ def closewidget(widget):
154
+ widget.destroy()
155
+ while Gtk.events_pending():
156
+ Gtk.main_iteration()
157
+
158
+
159
+
160
+ def main(argv = None):
161
+ if argv is None:
162
+ argv = sys.argv
163
+
164
+ view=MediaInfoView(argv[0])
165
+ view.fillTable(argv[1])
166
+ view.openView()
167
+
168
+
169
+ if __name__ == '__main__':
170
+ sys.exit(main())
171
+
@@ -0,0 +1,140 @@
1
+ # -*- coding: utf-8 -*-
2
+ '''
3
+ Created on 2025
4
+
5
+ @author: kanehekili
6
+ '''
7
+ import sys
8
+ import os
9
+ import gi
10
+ gi.require_version('Gtk', '4.0')
11
+ from gi.repository import Gtk, Gdk, GLib, Pango
12
+ from .MediaInfoGui import isHeader, formatForClipboard
13
+
14
+
15
+ class MediaInfoView(Gtk.ApplicationWindow):
16
+
17
+ def __init__(self, app, fileName):
18
+ super().__init__(application=app, title=fileName)
19
+ self.set_default_size(500, 600)
20
+
21
+ # Fallback header colours — overridden once the widget is realized
22
+ self.header_bg_rgba = Gdk.RGBA()
23
+ self.header_bg_rgba.parse("#3584e4")
24
+ self.header_fg_rgba = Gdk.RGBA()
25
+ self.header_fg_rgba.parse("#ffffff")
26
+ self.connect("realize", self._on_realize)
27
+
28
+ outer = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
29
+ outer.set_margin_start(5)
30
+ outer.set_margin_end(5)
31
+ outer.set_margin_top(5)
32
+ outer.set_margin_bottom(5)
33
+
34
+ frame = Gtk.Frame(label="Media Info")
35
+ frame.set_vexpand(True)
36
+ frame.set_child(self._buildTreeView())
37
+ outer.append(frame)
38
+
39
+ btn_bar = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
40
+ clip_btn = Gtk.Button(label="Clip")
41
+ clip_btn.connect("clicked", self._on_clip)
42
+ ok_btn = Gtk.Button(label="OK")
43
+ ok_btn.connect("clicked", self._on_ok)
44
+ spacer = Gtk.Box()
45
+ spacer.set_hexpand(True)
46
+ btn_bar.append(clip_btn)
47
+ btn_bar.append(spacer)
48
+ btn_bar.append(ok_btn)
49
+ outer.append(btn_bar)
50
+
51
+ self.set_child(outer)
52
+
53
+ def _on_realize(self, widget):
54
+ sc = self.treeView.get_style_context()
55
+ found_bg, bg = sc.lookup_color("theme_selected_bg_color")
56
+ found_fg, fg = sc.lookup_color("theme_selected_fg_color")
57
+ if found_bg:
58
+ self.header_bg_rgba = bg
59
+ if found_fg:
60
+ self.header_fg_rgba = fg
61
+ self.treeView.queue_draw()
62
+
63
+ def _buildTreeView(self):
64
+ self.store = Gtk.ListStore(str, str)
65
+ self.treeView = Gtk.TreeView(model=self.store)
66
+ self.treeView.set_grid_lines(Gtk.TreeViewGridLines.BOTH)
67
+ self.treeView.get_selection().set_mode(Gtk.SelectionMode.NONE)
68
+ self._addColumn("Item", 0)
69
+ self._addColumn("Data", 1)
70
+ sw = Gtk.ScrolledWindow()
71
+ sw.set_child(self.treeView)
72
+ sw.set_vexpand(True)
73
+ return sw
74
+
75
+ def _addColumn(self, header, col_id):
76
+ renderer = Gtk.CellRendererText()
77
+ column = Gtk.TreeViewColumn(header, renderer, text=col_id)
78
+ column.set_cell_data_func(renderer, self._headerCellData, col_id)
79
+ self.treeView.append_column(column)
80
+
81
+ def _headerCellData(self, column, renderer, model, iter, col_idx):
82
+ is_header = isHeader((model.get_value(iter, 0), model.get_value(iter, 1)))
83
+ if is_header:
84
+ renderer.set_property('cell-background-rgba', self.header_bg_rgba)
85
+ renderer.set_property('cell-background-set', True)
86
+ renderer.set_property('foreground-rgba', self.header_fg_rgba)
87
+ renderer.set_property('foreground-set', True)
88
+ renderer.set_property('weight', 700)
89
+ else:
90
+ renderer.set_property('cell-background-set', False)
91
+ renderer.set_property('foreground-set', False)
92
+ renderer.set_property('weight', 400)
93
+
94
+ def fillTable(self, rows):
95
+ for row in rows:
96
+ self.store.append(row)
97
+
98
+ def _on_clip(self, widget):
99
+ text = formatForClipboard([(r[0], r[1]) for r in self.store])
100
+ provider = Gdk.ContentProvider.new_for_bytes(
101
+ "text/plain;charset=utf-8",
102
+ GLib.Bytes.new(text.encode("utf-8"))
103
+ )
104
+ self.get_clipboard().set_content(provider)
105
+
106
+ def _on_ok(self, widget):
107
+ self.get_application().quit()
108
+
109
+
110
+ def showMessage(messageString):
111
+ app = Gtk.Application()
112
+
113
+ def on_activate(application):
114
+ dialog = Gtk.MessageDialog(
115
+ transient_for=None,
116
+ message_type=Gtk.MessageType.ERROR,
117
+ buttons=Gtk.ButtonsType.CLOSE,
118
+ text=messageString
119
+ )
120
+ dialog.connect("response", lambda d, r: application.quit())
121
+ dialog.present()
122
+
123
+ app.connect("activate", on_activate)
124
+ app.run(None)
125
+
126
+
127
+ def main(argv=None):
128
+ app = Gtk.Application()
129
+
130
+ def on_activate(application):
131
+ win = MediaInfoView(application, argv[0])
132
+ win.fillTable(argv[1])
133
+ win.present()
134
+
135
+ app.connect("activate", on_activate)
136
+ app.run(None)
137
+
138
+
139
+ if __name__ == '__main__':
140
+ sys.exit(main())
@@ -0,0 +1,125 @@
1
+ # -*- coding: utf-8 -*-
2
+ '''
3
+ Created on May 01 2020
4
+
5
+ @author: kanehekili
6
+ '''
7
+ import sys
8
+ import os
9
+ from PyQt6 import QtGui, QtWidgets, QtCore
10
+ from PyQt6.QtWidgets import QApplication, QErrorMessage, QMainWindow, QSizePolicy
11
+ from PyQt6.QtGui import QFont
12
+ from .MediaInfoGui import isHeader, formatForClipboard
13
+
14
+ class MediaInfoView(QMainWindow):
15
+
16
+ def __init__(self,fileName):
17
+ super(MediaInfoView,self).__init__()
18
+ self.initUI(fileName)
19
+
20
+ def initUI(self,fileName):
21
+ self.setWindowTitle(fileName)
22
+ palette = QApplication.palette()
23
+ self.hdr_bg = palette.color(QtGui.QPalette.ColorRole.Highlight)
24
+ self.hdr_fg = palette.color(QtGui.QPalette.ColorRole.HighlightedText)
25
+
26
+ #the icon
27
+ self.setWindowIcon(self.getAppIcon())
28
+
29
+ self.table = self.createListWidget()
30
+ clipBtn = QtWidgets.QPushButton("Clip")
31
+ clipBtn.clicked.connect(self.callback_copy)
32
+ okBtn = QtWidgets.QPushButton("OK")
33
+ okBtn.clicked.connect(self.callback_btn_ok)
34
+
35
+ buttonHBox = QtWidgets.QHBoxLayout()
36
+ buttonHBox.setContentsMargins(0, 0, 0, 0)
37
+ mainVBox = QtWidgets.QVBoxLayout()
38
+
39
+ buttonHBox.addWidget(clipBtn)
40
+ buttonHBox.addStretch()
41
+ buttonHBox.addWidget(okBtn)
42
+
43
+ mainVBox.addWidget(self.table)
44
+ mainVBox.addLayout(buttonHBox)
45
+
46
+ wid = QtWidgets.QWidget(self)
47
+ self.setCentralWidget(wid)
48
+ wid.setLayout(mainVBox)
49
+ self.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
50
+ self.setMinimumSize(500,600)
51
+ self.centerWindow()
52
+
53
+ def getAppIcon(self):
54
+ homeDir = os.path.dirname(__file__)
55
+ return QtGui.QIcon(os.path.join(homeDir,"mediainfo.png"))
56
+
57
+ def centerWindow(self):
58
+ screen = QApplication.primaryScreen().geometry()
59
+ frameGm = self.frameGeometry()
60
+ frameGm.moveCenter(screen.center())
61
+ self.move(frameGm.topLeft())
62
+
63
+ def createListWidget(self):
64
+ table = QtWidgets.QTableWidget()
65
+ font = QFont()
66
+ font.setPointSize(font.pointSize()-1)
67
+ table.setFont(font)
68
+ table.setColumnCount(2)
69
+ table.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarPolicy.ScrollBarAsNeeded)
70
+ table.setHorizontalHeaderLabels(["Item","Data"])
71
+ header = table.horizontalHeader()
72
+ header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeMode.ResizeToContents)
73
+ table.verticalHeader().setVisible(False)
74
+ table.verticalHeader().setStretchLastSection(True)
75
+ table.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.NoSelection)
76
+ table.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers)
77
+ table.setAlternatingRowColors(True)
78
+ return table
79
+
80
+ def fillTable(self, rows):
81
+ self.rows = rows
82
+ for row in rows:
83
+ self.addTextLine(row)
84
+
85
+ def addTextLine(self, strings):
86
+ row= self.table.rowCount()
87
+ self.table.insertRow(row)
88
+ col=0
89
+ is_header = isHeader(strings)
90
+ for item in strings:
91
+ qtitem = QtWidgets.QTableWidgetItem(item)
92
+ if is_header:
93
+ if col == 0:
94
+ font = QFont()
95
+ font.setBold(True)
96
+ qtitem.setFont(font)
97
+ qtitem.setBackground(QtGui.QBrush(self.hdr_bg))
98
+ qtitem.setForeground(QtGui.QBrush(self.hdr_fg))
99
+ self.table.setItem(row,col,qtitem)
100
+ col=col+1
101
+
102
+ # ------------ Callback section -----------------
103
+ def callback_copy(self):
104
+ QApplication.clipboard().setText(formatForClipboard(self.rows))
105
+
106
+ def callback_btn_ok(self):
107
+ QApplication.quit()
108
+
109
+
110
+ def showMessage(messageString):
111
+ app = QtWidgets.QApplication([])
112
+ msg = QErrorMessage()
113
+ msg.showMessage(messageString)
114
+ app.exec()
115
+
116
+
117
+ def main(argv = None):
118
+ app=QApplication(sys.argv)
119
+ view=MediaInfoView(argv[0])
120
+ view.fillTable(argv[1])
121
+ view.show()
122
+ app.exec()
123
+
124
+ if __name__ == '__main__':
125
+ sys.exit(main())
File without changes
@@ -0,0 +1,46 @@
1
+ Metadata-Version: 2.4
2
+ Name: mediainfogui
3
+ Version: 1.3.0
4
+ Summary: GUI for MediaInfo data - Qt6, GTK3 and GTK4
5
+ License-Expression: GPL-2.0-or-later
6
+ Project-URL: Homepage, https://github.com/kanehekili/MediaInfoGui
7
+ Requires-Python: >=3.9
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: PyQt6
10
+
11
+ # MediaInfoGui
12
+ GUI for MediaInfo data — available in Qt6, GTK3 and GTK4
13
+
14
+ Version 1.3.0
15
+
16
+ This simple GUI for the media info binary runs on Linux. Provides a fast & clean overview of the contents of a media file.
17
+
18
+ ##### Qt6 Implementation
19
+
20
+ ![Screenshot](./Screenshot-Qt5.png)
21
+
22
+ ##### GTK3 Implementation
23
+
24
+ ![Screenshot](./Screenshot-GTK3.png)
25
+
26
+ ### Features
27
+ - Opens on a given filename, usually from a file manager
28
+ - Shows codec data for video, audio and images
29
+ - Displays full MPEG-TS program structure (PAT/PMT), including broken or unresolvable programs — requires `ffprobe`
30
+ - Section headers highlighted with the theme's selection colour
31
+ - Copy to clipboard button ("Clip") for pasting media info into other applications
32
+ - Supports light and dark themes
33
+
34
+ ### Prerequisites
35
+ - `mediainfo`
36
+ - Qt6: `python3-pyqt6` (PyQt6)
37
+ - GTK3: `python3-gi` with GTK 3
38
+ - GTK4: `python3-gi` with GTK 4
39
+ - Optional: `ffprobe` (from ffmpeg) for MPEG-TS program info
40
+
41
+ ### How to install
42
+ * Download the MediaInfoGui*.tar contained in the "build" folder
43
+ * Unpack it and run the command **sudo ./install.sh** in the unpacked folder.
44
+ * Select 1 for Qt6, 2 for GTK3, or 3 for GTK4
45
+ * Install just copies a desktop file and some python scripts to /usr/local/sbin/MediaInfoGui
46
+
@@ -0,0 +1,15 @@
1
+ MANIFEST.in
2
+ README.md
3
+ pyproject.toml
4
+ mediainfogui/MediaInfoGui.py
5
+ mediainfogui/MediaInfoWidgetsGTK3.py
6
+ mediainfogui/MediaInfoWidgetsGTK4.py
7
+ mediainfogui/MediaInfoWidgetsQt.py
8
+ mediainfogui/__init__.py
9
+ mediainfogui/mediainfo.png
10
+ mediainfogui.egg-info/PKG-INFO
11
+ mediainfogui.egg-info/SOURCES.txt
12
+ mediainfogui.egg-info/dependency_links.txt
13
+ mediainfogui.egg-info/entry_points.txt
14
+ mediainfogui.egg-info/requires.txt
15
+ mediainfogui.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ mediainfogui = mediainfogui.MediaInfoGui:main
@@ -0,0 +1 @@
1
+ mediainfogui
@@ -0,0 +1,23 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "mediainfogui"
7
+ version = "1.3.0"
8
+ description = "GUI for MediaInfo data - Qt6, GTK3 and GTK4"
9
+ readme = "README.md"
10
+ license = "GPL-2.0-or-later"
11
+ requires-python = ">=3.9"
12
+ dependencies = ["PyQt6"]
13
+
14
+ [project.urls]
15
+ Homepage = "https://github.com/kanehekili/MediaInfoGui"
16
+
17
+ [project.scripts]
18
+ mediainfogui = "mediainfogui.MediaInfoGui:main"
19
+
20
+ [tool.setuptools.package-data]
21
+ mediainfogui = [
22
+ "*.png",
23
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+