Jump to content

Using comskip


Recommended Posts

Posted (edited)

Just thought i shared this if anyone wanted to use it:

I was looking into automating hiding commercials from my recordings my requirements:

  • Has to work in linux (In my case fedora)
  • Emby should trigger it after the recording is done (command line)

  • Mark commercials as chapters and not remove them, I dont need a copy of the file if the commercial are identified incorrectly

I found the following free software to accomplish this:

  • comskip (easy to build in linux and also available already compiled for windows and other OS)
  • mkvtoolnix (widely available for a multitude of distributions precompiled)

  • python (easy to program with but can be changed to you programming of choice)

How it works:

emby starts a python script after a recording is finished,. (the recording has to saved as mkv)

python script:

  • run the video through comskip and produes an edl file with the location of the commercials
  • create a list of chapters based on the edl file 

  • create one chapter list with only the chapters that dont have commercials and a list with all chapters

  • run the video through mkvpropedit , edits the header or the file (fast operation) and adds two editions on the file one without commercial and one with commercials.

For now if you watch the video through emby only the chapters are recognized but it wont skip commercials as ffmpeg doesnt support ordered chapters. If you watch the video through vlc or kodi it selects by default the no commercial track and plays fine.

#!/usr/bin/env python3
import os
import xmltodict
import datetime
import subprocess
import sys
	
		
class EDLEntry:
	def __init__(self, entryLine):
		print(entryLine)
		a = entryLine.strip().split(None,3);
		print(a)
		self.Start = float(a[0])
		self.End = float(a[1])
		self.Type = float(a[2])
	
	def __lt__(self, other):
		if hasattr(other, 'Start'):
			return self.Start < other.Start
class EDL:
	def __init__(self, filepath):
		self.Entries = []
		self.Entries.extend([EDLEntry(line) for line in open(filepath)])
		
	def GetChapters(self):
		chapters = []
		firstIsCommercial = False
		sorted(self.Entries)
		i = 0
		l = len(self.Entries)
		if l==0:
			return chapters
		if self.Entries[0].Start != 0.0:
			chapters.append(Chapter(0,"Show",self.Entries[0].Start))
		for comercial in self.Entries:
			i += 1
			show = Chapter(comercial.End,"Show")
			if i < l:
				show.End = self.Entries[i].Start
				chapters.extend([
					Chapter(comercial.Start,"Comercial",comercial.End),
					show
				])
			else:
				chapters.extend([Chapter(comercial.Start,"Comercial",comercial.End)])
		
		return chapters
		
def getTimespanFromSeconds(s = 0):
	return "{:0>8}".format(str(datetime.timedelta(seconds=s)))
	
class Chapter:
	def __init__(self, start=0.0, title = "Chapter", end = None, lang = None):
		self.Start = start
		self.Title = title
		self.End = end
		self.Lang = lang
		
	def GenerateChapterAtom(self, i = None):
		xml = {
			'ChapterFlagEnabled':1,
			'ChapterTimeStart':getTimespanFromSeconds(self.Start),
			'ChapterDisplay':{
				'ChapterString': self.Title
			}
		}
		if self.Lang !=  None:
			xml["ChapterDisplay"]["ChapterLanguage"] = lang
		if self.End != None:
			xml["ChapterTimeEnd"] = getTimespanFromSeconds(self.End),
		return xml
			
	def __lt__(self, other):
		if hasattr(other, 'Start'):
			return self.Start < other.Start
class EditionEntry:
	def __init__(self, chapters = None, uid = None):
		self.Chapters = chapters if chapters != None else []
		self.UID = uid
	def GetEditionEntry(self):
		return {'ChapterAtom':[chapter.GenerateChapterAtom() for chapter in sorted(self.Chapters)],'EditionFlagOrdered':1,'EditionUID':self.UID}
	
class Video:
	def __init__(self):
		self.Editions = []
		self.Tags = {"Tags":{'Tag':[]}}
		
	def AddEdition(self, chapters, title):
		uid = len(self.Editions)+1
		self.Editions.append(EditionEntry(chapters,uid))
		self.Tags["Tags"]['Tag'].append({
			"Targets":{
				"EditionUID":uid,
				"TargetTypeValue":50
			},
			"Simple":{
				"Name":"TITLE",
				"String":title,
				"TagLanguage":"eng",
				"DefaultLanguage":1
			}
		})
	
	def Export(self, filepath):
		dict = {"Chapters":{"EditionEntry":[edition.GetEditionEntry() for edition in self.Editions]}}
		result = xmltodict.unparse(dict, encoding="ISO-8859-1", pretty=True)
		with open(filepath+".chapters", "w") as text_file:
			print(result, file=text_file)
		result = xmltodict.unparse(self.Tags, encoding="ISO-8859-1", pretty=True)
		with open(filepath+".tags", "w") as text_file:
			print(result, file=text_file)

if __name__== "__main__":
	name = sys.argv[1]
	inipath = os.path.splitext(sys.argv[2])[0]+".ini"
	path = os.path.splitext(name)[0]
	try:
		with subprocess.Popen("/usr/local/bin/comskip --ini='\"{1}\"' '{0}.mkv'".format(path,inipath), shell=True,stdout=subprocess.PIPE,) as p:
			for line in p.stdout:
				print(line.decode(), end='')
		chapters = EDL(path+".edl").GetChapters()
		v = Video()
		v.AddEdition([chapter for chapter in chapters if chapter.Title == "Show"],'Without Commercials')
		v.AddEdition(chapters,'With Comercials')
		v.Export(path)	
		with subprocess.Popen("/bin/mkvpropedit --chapters '' '{0}.mkv'".format(path), shell=True,stdout=subprocess.PIPE) as p:
			for line in p.stdout:
				print(line.decode(), end='')
		with subprocess.Popen("/bin/mkvpropedit -t all:'{0}.tags' --chapters '{0}.chapters' '{0}.mkv'".format(path), shell=True,stdout=subprocess.PIPE) as p:
			for line in p.stdout:
				print(line.decode(),end='')
	except:
		print("Unexpected error:", sys.exc_info()[0])
  
  
  
  
Edited by jose
  • Like 2
  • 6 months later...
Posted

This looks really close to what I am trying to do.  Would you be willing to assist me in adapting this to my use case?  I would like to:

1.  use comskip to identify commercial segments

2.  use mkvmerge to split the video.

3.  merge the video without the commercials. 

 

I run Emby on Windows 10, and have installed Comskip, Python and mkvtoolnix on my system.  I assume that I need to take your script and put it into a .py script. 

  • Like 1
Posted

@@Tur0k

 

Can you PM me a copy of a .edl or .xml file you have created with comskip.  I'm pretty sure i can get this to work with ffmpeg.

  • Like 2
  • 5 months later...
Posted

@@Tur0k

 

Can you PM me a copy of a .edl or .xml file you have created with comskip.  I'm pretty sure i can get this to work with ffmpeg.

Any luck merging a commercial free mkv? I use MCEBuddy with comskip in Windows t trim my commericals. It's the only thing holding me back from switching my media server to Linux.

 

Thanks

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...