.opus scanner?

Does anyone know of a scanner that works for .opus files? I have quite a few that I want to add to my Library, but plex has been fighting me at every turn. I know you can add a new audio_ext to AudioFiles.py, but this soon gets overwritten. When it is able to pull them in, it refuses to even read the metadata that I spent so long making sure was right. This is a regular ogg file, so it should just be able to use the same setup. (Which doesn't work, sadly...)

 

I threw together some code that shows it should be possible to extra the metadata info from .opus files with mutagen. I got the idea because there's already an oggopus.py parser included with the install, but no clue how to add this into a custom scanner. Any help would be greatly appreciated!

 

(Btw, put this code in a directory with .opus files)

#!/usr/bin/env python

import os, sys

from mutagen import File as MFile
#import mutagen.mp3
#import mutagen.id3
#import mutagen.flac
#import mutagen.oggvorbis
#import mutagen.oggopus

TYPES = {
‘mp3’: ‘MP3’,
‘aac’: ‘AAC’,
‘alac’: ‘ALAC’,
‘ogg’: ‘OGG’,
‘opus’: ‘Opus’,
‘flac’: ‘FLAC’,
‘ape’: ‘APE’,
‘wv’: ‘WavPack’,
‘mpc’: ‘Musepack’,
‘asf’: ‘Windows Media’,
‘aiff’: ‘AIFF’,
}

workingDirectory = “./”

fileListMetadata =

Get list of files in directory

fileList = os.listdir(workingDirectory)

Clean up fileList to be in order

fileList.sort()

Scan through the fileList and remove those which are not audio files

for filename in fileList:
Metadata =
tag = MFile(filename, None, True)
file, fileExtension = os.path.splitext(filename) # read file extensions
if fileExtension[1:] not in TYPES: # clean out files that are not audio files
fileList.remove(filename) # if it’s not an audio file, we don’t want it
else:
if type(tag).name is ‘OggOpus’:
# Extract metadata
artist = (str(MFile(filename, None, True)[‘ARTIST’])) # might need [3:-2] to clean up tags??
album = (str(MFile(filename, None, True)[‘ALBUM’]))
title = (str(MFile(filename, None, True)[‘TITLE’]))
track = (str(MFile(filename, None, True)[‘TRACKNUMBER’]))
disc = (str(MFile(filename, None, True)[‘DISCNUMBER’]))
TPE2 = (str(MFile(filename, None, True)[‘ALBUMARTIST’]))

        Metadata.append(filename)
        Metadata.append(artist)
        Metadata.append(album)
        Metadata.append(title)
        Metadata.append(track)
        Metadata.append(disc)
        Metadata.append(TPE2)
        
# Add the set to the list of metadata
fileListMetadata.append(Metadata)

print fileListMetadata

1 Like

Don’t have much time to code it and maintain but thought i would give pointers. the code above should allow for a quick hack of existing music scanners easilly

Here is an untested attempt at a scanner. i will not maintain it nor test

import os, sys, Media
import logging                   # Python       - logging.basicConfig
from mutagen import File as MFile
Virtual = True

### setup logging https://docs.python.org/2/library/logging.html ###  #logging.debug/info/warning/error/critical('some critical message: %s', 'highest category')
try:      platform = sys.platform.lower()  # sys.platform: win32 | darwin | linux2, 
except:   platform="other"
#  try:    platform = Platform.OS.lower()   # Platform.OS:  Windows, MacOSX, or Linux
#  except: p5latform = ""
if   (platform == 'win32'  or platform == 'windows'): LOG_PATH = os.path.expandvars( '%LOCALAPPDATA%\\Plex Media Server\Logs' )
elif (platform == 'darwin' or platform == 'macosx'):  LOG_PATH = os.path.expandvars( '$HOME/Library/Application Support/Plex Media Server\Logs' )
elif 'linux' in platform:                             LOG_PATH = os.path.expandvars( '$PLEX_HOME/Library/Application Support/Plex Media Server\Logs' )                          
if not os.path.isdir(LOG_PATH):                       LOG_PATH = os.path.expanduser('~')
logging.basicConfig(filename=os.path.join(LOG_PATH, LOG_FILE), format=LOG_FORMAT, level=logging.DEBUG)  

### Log function ########################################################################################
def Log(entry, filename='Plex Media Scanner Custom.log'): #need relative path
  logging.info(entry + "\r\n") # allow to use windows notepad with correct line feeds
  print entry                  # when ran from console

### Add files into Plex database ########################################################################
def explore_path(subdir, file_tree):
  files=[]
  for item in os.listdir(subdir): 
    fullpath = os.path.join(subdir, item)
    if os.path.isdir (fullpath): 
      for rx in ignore_dirs_re_findall: ### Skip unwanted folders ###
        result = re.findall(rx, item)   
        if len(result):  break
      else:  explore_path(fullpath, file_tree)     
    elif os.path.isfile(fullpath):
      fileName, fileExtension = os.path.splitext(item)
      if fileExtension[1:] in video_exts:
        for rx in ignore_files_re_findall:  ### Filter trailers and sample files ###
          result = re.findall(rx, item)
          if len(result): 
            Log("'%s' ignore_files_findall: match" % item)
            break
        else:  files.append(fullpath) ### Retain wanted file extensions ###
  if not files == []:  file_tree[subdir] = files

def Scan(path, files, mediaList, subdirs, language=None, root=None):
  #if not path == "":  return  # Exit every other iteration than the root scan
  if not root:  return
  
  TYPES = {'mp3':  'MP3', 'aac':  'AAC', 'alac':  'ALAC', 'ogg':  'OGG', 'opus': 'Opus', 'flac': 'FLAC',
           'ape':  'APE', 'wv':   'WavPack', 'mpc':  'Musepack', 'asf':  'Windows Media', 'aiff': 'AIFF', }

  Log("=== Scan ================================================================================================================")
  Log("Platform: '%s', Test location: '%s'" % (platform, LOG_PATH))
  Log("Scan: (root: '%s', path='%s', subdirs: '%s', Files: '%s', language: '%s')" % (root if root is not None else "", path, str(subdirs), str(files), language))
  Log("=========================================================================================================================")
  
  file_tree = {}                                           # initialize file_tree
  explore_path(root, file_tree)                            # initialize file_tree with files on root
  for path, files in file_tree.iteritems():                # Loop to add all series while on the root folder Scan call, which allows subfolders to work
    subdirs=[]                                             # Recreate normal scanner coding: subfolders empty
    path   = path.replace(root, "")                        # Recreate normal scanner coding: path is relative to root
    if path.startswith("/"):  path = path[1:]              # Recreate normal scanner coding: path doesn't start with "/"   
    relative_path = path.replace(root, " ")                # Foe exemple /group/serie/season/ep folder
    reverse_path  = Utils.SplitPath(relative_path)         # Take top two as show/season, but require at least the top one.
    reverse_path.reverse()                                 # Reverse the order of the folders
  
    for filename in files:
      file, fileExtension = os.path.splitext(filename)              # read file extensions
      if fileExtension[1:] not in TYPES:  continue                  # clean out files that are not audio files. if it's not an audio file, we don't want it
      elif type(MFile(filename, None, True)).__name__ is 'OggOpus': # Extract metadata
        plex_track = Media.Track(
          artist       = str(MFile(filename, None, True)['ARTIST'     ]).encode("utf-8"), # might need [3:-2] to clean up tags??
          album        = str(MFile(filename, None, True)['ALBUM'      ]).encode("utf-8"),
          title        = str(MFile(filename, None, True)['TITLE'      ]).encode("utf-8"),
          album_artist = str(MFile(filename, None, True)['ALBUMARTIST']).encode("utf-8"),
          index        = str(MFile(filename, None, True)['TRACKNUMBER']).encode("utf-8"),
          disc         = str(MFile(filename, None, True)['DISCNUMBER' ]).encode("utf-8"),
        )
        plex_track.parts.append(filename)
        mediaList.append(plex_track)

Best would be to take the original scanner init.py and AudioFiles.py and edit relevant parts
It already accepts opus files, just the supprot for the internal meta has to be added

1 Like