Script to create NFO files
#1
Hi all, because the "manual add to library" option no longer exists, I decided to write a script (in PowerShell, sorry Linux people) to create NFO files that can then be used to add stuff to the library.
Basically, all it does is go through all the movie paths and then go through all the movies in those paths and if it finds a movie in the file system that is not in the XBMC database, it creates an NFO file. I use it a lot of home movies, and the unusual (i.e. never to be found in a movie database!) videos I have. I can then re-scan and it pops them into the library. Simple! No TV support yet - just movies.

Question for the moderators: where should I post it? It's not a plug in as such as it just reads the DB directly and file system.
Reply
#2
OK, well, I'll post it here anyway and, mods, please move it to the right place. TVM
You'll need to install ADO.NET adapter for SQLite first (Google it - it's the top link usually.) If you don't read this instruction, don't worry, the error will prompt you to install it. Wink

EDIT 20121211: This is for XBMC version 11.x only. Currently under development to extend it to version 12.
EDIT 20121211: Vista and Windows 7 only. It might work for earlier versions of Windows. Probably won't work in portable mode either.
EDIT 20121216: Added Frodo support and a few other bits and pieces.

Code:
# Title:   Mossywell's NFO File Creator
# Version: 0.2
# Date:    20121216
# Support: You're having a laugh, right? That's why I did it in powershell. :)
# Notes:   0.1 - First test release onto the XBMC forums.
#          0.2 - Stricter type handling.
#              - Added Frodo support via enumeration (probably unnecessary frippery!)
# To Do:  - Remove the duplicated code - there's far too much of it! - DONE PARTIALLY
#         - Regional strings.
#         - Catches should be more specific.
#         - Support for TV shows. (I know the NFO format, but not the database format because I don't have any TV shows!)

# Eunms (see http://connect.microsoft.com/PowerShell/feedback/details/742760/add-type-cannot-add-type-the-type-name-already-exists)
try
{
Add-Type -TypeDefinition @"
   public enum XBMCVersion
   {
      Null,
      Eden,
      Frodo
   }
"@
}
catch
{
  # Try already exists (see HTTP link above)
}

$ErrorActionPreference = "Stop"

Function getSQLiteData($profile, $query)
{
  # Return: System.Data.DataTable as System.Object[]
  
  [string]$sqlitedll = [Environment]::GetEnvironmentVariable("ProgramFiles") + "\System.Data.SQLite\2008\bin\System.Data.SQLite.dll"
  try
  {
    [void][System.Reflection.Assembly]::LoadFrom($sqlitedll)
  }
  catch
  {
    Write-Host $("ERROR: Unable to load the SQLite DLL """ + $sqlitedll + """. Is the ADO.NET adapter for SQLite installed?") -Foreground Red
    Exit
  }
  
  # Which DB file?
  switch($xbmcversion)
  {
    ([XBMCVersion]::Eden)
    {
      $dbfile = "MyVideos60.db"
    }
    ([XBMCVersion]::Frodo)
    {
      $dbfile = "MyVideos75.db"
    }    
  }

  # Create the connection object
  $conn = New-Object -TypeName System.Data.SQLite.SQLiteConnection

  # Create the connection string, query, data table, adapter
  if($profile -eq "Master user")
  {
    $connectionstring = "Data Source=" + $([Environment]::GetEnvironmentVariable("UserProfile")) + "\AppData\Roaming\XBMC\userdata\Database\$dbfile"
  }
  else
  {
    $connectionstring = "Data Source=" + $([Environment]::GetEnvironmentVariable("UserProfile")) + "\AppData\Roaming\XBMC\userdata\profiles\" + $profile + "\Database\$dbfile"
  }
  $conn.ConnectionString = $connectionstring
  [System.Data.DataTable]$table = New-Object System.Data.DataTable
  $adapter = New-Object System.Data.SQLite.SQLiteDataAdapter($query, $conn)

  # Let's do it - the table will contain the results
  try
  {
    $adapter.Fill($table) > $null
  }
  catch
  {
    Write-Host $("ERROR: The SQL query failed for connection string """ + $connectionstring + """. Does the file exist and is it accessible?") -Foreground Red
    Exit
  }
  $conn.Close()
  
  # Return the table
  $table
}

Function IsInMovieLibrary($path)
{
  # Default is not found
  [bool]$found = $false
  
  foreach($row in $moviepaths)
  {
    # Case insensitive
    if($path -eq $row[0])
    {
      $found = $true
      break
    }
  }
  
  $found
}

Function generateNfos([string]$searchpath, [bool]$recurse)
{
  # Constants
  # Following can be edited to include other video file types
  Set-Variable videofiletypes -Option Constant -Value @("*.avi", "*.mpg", "*.flv", "*.wmv", "*.mp4", "*.mkv")
  
  # Variables
  [int]$nfos = 0
  
  # For each directory...
  if($recurse)
  {
    $dirs = Get-ChildItem -Path $searchpath -Recurse | where {$_.PSIsContainer}
  }
  else
  {
    $dirs = Get-ChildItem -Path $searchpath | where {$_.PSIsContainer}
  }
  
  if($dirs -ne $null)
  {    
    foreach($dir in $dirs)
    {    
      # Only create an NFO if it's not in the library already (directory must have a "\" at the end)
      if(-not (IsInMovieLibrary($dir.FullName + "\")))
      {
        # Only do it if there's a VIDEO_TS subfolder - it's a DVD
        if(Test-Path -Path $($dir.FullName + "\VIDEO_TS"))
        {
          [string]$s  = "<?xml version=""1.0"" encoding=""utf-8""?>" + $nl
          $s += "  <movie>" + $nl
          $s += "    <title>$($dir.BaseName)</title>" + $nl
          $s += "    <id>-1</id>" + $nl
          $s += "  </movie>"
          [string]$outfile = $dir.FullName + "\movie.nfo"
          # Output to the host NOT the pipeline!
          Write-Host $("Creating NFO file: """ + $outfile + """")
          try
          {
            Set-Content -Encoding UTF8 -Path $outfile -Force -Value $s
            $nfos++
          }
          catch
          {
            Write-Host $("ERROR: Unable to create NFO file """ + $outfile + """. Do you have permissons and is there sufficient disk space?") -Foreground Red
            Exit
          }
        }
      }
    }
  }

  # For each video file...
  if($recurse)
  {
    $files = Get-ChildItem -Path $($searchpath + "*") -Recurse -Include $videofiletypes | where {!$_.PSisContainer}
  }
  else
  {
    $files = Get-ChildItem -Path $($searchpath + "*") -Include $videofiletypes | where {!$_.PSisContainer}
  }
  
  if($files -ne $null)
  {    
    foreach($file in $files)
    {
      # Only create an NFO if it's not in the library already
      if(-not (IsInMovieLibrary($file.FullName)))
      {    
        [string]$s  = "<?xml version=""1.0"" encoding=""utf-8""?>" + $nl
        $s += "  <movie>" + $nl
        $s += "    <title>$($file.BaseName)</title>" + $nl
        $s += "    <id>-1</id>" + $nl
        $s += "  </movie>"
        [string]$outfile = $file.DirectoryName + "\" + $file.BaseName + ".nfo"
        # Output to the host NOT the pipeline!
        Write-Host $("Creating NFO file: """ + $outfile + """")
        try
        {
          Set-Content -Encoding UTF8 -Path $outfile -Force -Value $s
          $nfos++
        }
        catch
        {
          Write-Host $("ERROR: Unable to create NFO file """ + $outfile + """. Do you have permissons and is there sufficient disk space?") -Foreground Red
          Exit
        }
      }
    }
  }
  
  $nfos
}

Function getProfiles()
{
  # Return: System.Object[]
  
  # Load the profiles.xml file
  [string]$profilefile = [Environment]::GetEnvironmentVariable("UserProfile") + "\AppData\Roaming\XBMC\userdata\profiles.xml"
  try
  {
    # Recast
    [xml]$profilefile = Get-Content $profilefile
  }
  catch
  {
    Write-Host $("ERROR: Unable to access """ + $profilefile + """.") -Foreground Red
    Exit
  }
  
  # Pass to the pipeline the profile names into an array
  @($profilefile.profiles.profile | % {$_.name})
}

Function getInstallLocation()
{
  # Return: [string] Location of the XBMC loation from the registry
  [string]$location = ""
  try
  {
    $location = Get-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\XBMC" | % { $_.InstallLocation }
  }
  catch
  {
    Write-Host "ERROR: Unable to get the XBMC installation location. Is it installed?" -Foreground Red
    Exit
  }
  
  # Return
  $location
}

Function getXBMCVersion([string]$location)
{
  # Return: [XBMCVersion] The Exe major version from the file properties
  [XBMCVersion]$xbmcver = [XBMCVersion]::Null
  
  [string]$exefile = $location + "\XBMC.exe"

  [string]$ver = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($exefile).FileVersion
  $ver = ($ver.Split("."))[0]
  
  switch($ver)
  {
    "11"
    {
      $xbmcver = [XBMCVersion]::Eden
    }
    "12"
    {
      $xbmcver = [XBMCVersion]::Frodo
    }
  }
  
  # Return
  $xbmcver
}


#
# Main
#
# Constants
Set-Variable nl -Option Constant -Value $([Environment]::NewLine)

# Variables
[int]$nfocount = 0
[string]$query = ""

# Get the install location
[XBMCVersion]$xbmcversion = (getXBMCVersion (getInstallLocation))
"XBMC Major Version: " + $xbmcversion.ToString()

# Which user are we interested in?
$profiles = getProfiles

# Ask the user which profile we are interested in
[int]$i = 1
$($nl + "The following XBMC profiles were found:")
foreach($profile in $profiles)
{
  # $profile is [string]
  
  $i.ToString() + ": " + $profile
  $i++
}
Do {$selectedprofile = Read-Host "Enter the user you wish to create NFO files for and press Return"} while((1..$($profiles.Count)) -notcontains $selectedprofile)

# Now grab the movie paths and search paths for that user
$query = "SELECT strPath,strContent,strHash,scanRecursive FROM path WHERE strContent = ""movies"" OR (strContent = ""tvshows"" AND strHash IS NOT NULL)"
$searchpaths = getSQLiteData $profiles[$selectedprofile - 1] $query
$query = "SELECT c22 FROM movie"
$moviepaths = getSQLiteData $profiles[$selectedprofile - 1] $query

# Iterate each search path
foreach($path in $searchpaths)
{
  # $path is System.Data.DataRow
  
  # Check if the original path contained the "recurse" instruction
  if($path[3] -gt 0)
  {
    $retval = generateNfos ($path[0].ToString()) $true
  }
  else
  {
    $retval = generateNfos ($path[0].ToString()) $false
  }
  $nfocount += $retval
}
"NFO files created: " + $nfocount
Reply
#3
I Tried the script on windows8 64bit (dutch) with XBMC 12 Beta 3 and the output of the script is "ERROR: The SQL query failed for connection string "Data Source=C:\Users\info_000\AppData\Roaming\XBMC\userdata\profiles\M\Database\MyVideos60.db". Does the file exist and
is it accessible?"

The path isn't present is should have bin "C:\Users\info_000\AppData\Roaming\XBMC\userdata\Database\MyVideos75"
Off course i tried with run as administrator.
Reply
#4
I think MyVideos60=Eden, MyVideos75=Frodo. Script should probably look for that and set appropriate version. Also it seems like it should somehow use special://profile, as written appears to be hard coded to look in Win Vista/7/8 user appdata.

scott s.
.
Reply
#5
Newer knew that it was possible to identify the XBMC version by number of the db, i only tried it on my developer XBMC machine.
Reply
#6
@djoeney yes, fair point, it is only designed to work on 11.x because that's all I have, so I'll make that clear in the OP. It shouldn't be too difficult to adapt it for 12.x though, once I've worked out the best way to determine the version programatically.

@scott967: yes, it does look in the Windows user's profile location. But, the script is for Windows, which knows nothing of the "special://" protocol, not XBMC. If it were an addon then yes, I'd use the special protocol of course.
Reply
#7
(2012-12-11, 11:26)mossywell Wrote: @scott967: yes, it does look in the Windows user's profile location. But, the script is for Windows, which knows nothing of the "special://" protocol, not XBMC. If it were an addon then yes, I'd use the special protocol of course.

Got it. Not an issue for me, just noticed as I was looking at it. Don't think it will work for XP or XBMC in portable mode. But probably not an issue for 99%.

scott s.
.
Reply
#8
(2012-12-11, 21:12)scott967 Wrote:
(2012-12-11, 11:26)mossywell Wrote: @scott967: yes, it does look in the Windows user's profile location. But, the script is for Windows, which knows nothing of the "special://" protocol, not XBMC. If it were an addon then yes, I'd use the special protocol of course.

Got it. Not an issue for me, just noticed as I was looking at it. Don't think it will work for XP or XBMC in portable mode. But probably not an issue for 99%.

scott s.
.
If someone is willing to try - cool. Not that I'll be installing XP in a hurry to fix it if it doesn't work. Smile I'll add this to the pre-reqs.
Reply
#9
@djoeney: Version 0.2 adds Frodo support. Just tried it on RC1 and it worked fine.
Reply

Logout Mark Read Team Forum Stats Members Help
Script to create NFO files1