From 31b8602f529f573f1ab31247341d6e0892650f7b Mon Sep 17 00:00:00 2001 From: Sven Slootweg Date: Wed, 13 Apr 2016 10:59:16 +0200 Subject: [PATCH] Initial implementation of filesets --- src/components/fileset-browser/component.tag | 13 ++++ src/filesets/find-matching-basenames.js | 22 ++++++ src/filesets/index.js | 70 ++++++++++++++++++++ src/filesets/plugins/generic-subtitles.js | 31 +++++++++ src/filesets/plugins/youtube-dl.js | 44 ++++++++++++ src/filesystem/list-directory.js | 5 +- src/path/extension-matches.js | 6 ++ src/path/in-extensions.js | 16 +++++ src/path/subtract-extension.js | 6 ++ 9 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 src/filesets/find-matching-basenames.js create mode 100644 src/filesets/index.js create mode 100644 src/filesets/plugins/generic-subtitles.js create mode 100644 src/filesets/plugins/youtube-dl.js create mode 100644 src/path/extension-matches.js create mode 100644 src/path/in-extensions.js create mode 100644 src/path/subtract-extension.js diff --git a/src/components/fileset-browser/component.tag b/src/components/fileset-browser/component.tag index 7590a51..3777d90 100644 --- a/src/components/fileset-browser/component.tag +++ b/src/components/fileset-browser/component.tag @@ -3,6 +3,8 @@ fileset-browser .entry(each="{entry in entries}", class="{folder: entry.type === 'folder'}", onclick="{handleClickEntry}") span.filename {entry.name} span.size(hide="{entry.type === 'folder'}") {entry.size} + ul.setItems(show="{entry.setItems != null}") + li(each="{setItem in entry.setItems}" class="setItem") {setItem.name} style(scoped, type="scss"). .browser { @@ -35,6 +37,17 @@ fileset-browser float: right; color: gray; } + + ul.setItems { + list-style-type: none; + padding: 0px 0px 0px 8px; + margin: 2px 0px 2px 0px; + } + + li.setItem { + color: gray; + font-size: 12px; + } } } diff --git a/src/filesets/find-matching-basenames.js b/src/filesets/find-matching-basenames.js new file mode 100644 index 0000000..3561848 --- /dev/null +++ b/src/filesets/find-matching-basenames.js @@ -0,0 +1,22 @@ +'use strict'; + +const rfr = require("rfr"); +const inExtensions = rfr("lib/path/in-extensions"); +const subtractExtension = rfr("lib/path/subtract-extension"); + +module.exports = function(filename, extension, candidateFilenames, candidateExtensions) { + let basename = subtractExtension(filename, extension); + + return candidateFilenames.map((candidate) => { + return { + filename: candidate, + extensionMatch: inExtensions(candidate, candidateExtensions) + } + }).filter((candidate) => { + return candidate.extensionMatch !== false; + }).filter((candidate) => { + return (subtractExtension(candidate.filename, candidate.extensionMatch) === basename); + }).map((candidate) => { + return candidate.filename; + }); +} \ No newline at end of file diff --git a/src/filesets/index.js b/src/filesets/index.js new file mode 100644 index 0000000..cb95011 --- /dev/null +++ b/src/filesets/index.js @@ -0,0 +1,70 @@ +'use strict'; + +const xtend = require("xtend"); +const rfr = require("rfr"); + +let plugins = [ + rfr("lib/filesets/plugins/youtube-dl"), + rfr("lib/filesets/plugins/generic-subtitles") +]; + +function attemptPairing(item, allItems) { + for (let i = 0; i < plugins.length; i++) { + let result = plugins[i](item, allItems); + + if (result.length > 0) { + return result; + } + } + + return []; +} + +module.exports = function(items, options) { + let indexedItems = items.reduce((total, item) => { + total[item.name] = item; + return total; + }, {}); + + let pairedItems = []; + let finalItems = []; + + items.forEach((item) => { + if (pairedItems.indexOf(item.name) !== -1) { + return; + } + + let pairing = attemptPairing(item, indexedItems).map((pairedItemName) => { + return indexedItems[pairedItemName]; + }); + + if (pairing.length > 0) { + pairedItems.push(item.name); + + pairing.forEach((pairedItem) => { + pairedItems.push(pairedItem.name); + }); + + if (options.sorter != null) { + pairing.sort(options.sorter); + } + + finalItems.push(xtend(item, { + setItems: pairing + })); + } + }); + + // Add all unpaired items to the final list. + items.forEach((item) => { + if (pairedItems.indexOf(item.name) === -1 && item.setItems == null) { + finalItems.push(item); + } + }) + + if (options.sorter != null) { + finalItems.sort(options.sorter); + } + + return finalItems; +} \ No newline at end of file diff --git a/src/filesets/plugins/generic-subtitles.js b/src/filesets/plugins/generic-subtitles.js new file mode 100644 index 0000000..9752971 --- /dev/null +++ b/src/filesets/plugins/generic-subtitles.js @@ -0,0 +1,31 @@ +'use strict'; + +const rfr = require("rfr"); +const inExtensions = rfr("lib/path/in-extensions"); +const findMatchingBasenames = rfr("lib/filesets/find-matching-basenames"); + +module.exports = function(item, allItems) { + let matchExtensions = [ + ".mp4", + ".mkv", + ".flv", + ".avi", + ".ogv", + ".mpeg", + ".wmv" + ] + + let setExtensions = [ + ".vtt", + ".srt", + ".sub" + ]; + + let match = inExtensions(item.name, matchExtensions); + + if (match) { + return findMatchingBasenames(item.name, match, Object.keys(allItems), setExtensions); + } else { + return []; + } +} \ No newline at end of file diff --git a/src/filesets/plugins/youtube-dl.js b/src/filesets/plugins/youtube-dl.js new file mode 100644 index 0000000..6db7a8f --- /dev/null +++ b/src/filesets/plugins/youtube-dl.js @@ -0,0 +1,44 @@ +'use strict'; + +const rfr = require("rfr"); +const inExtensions = rfr("lib/path/in-extensions"); +const findMatchingBasenames = rfr("lib/filesets/find-matching-basenames"); + +module.exports = function(item, allItems) { + let matchExtensions = [ + ".mp4", + ".mkv", + ".flv" + ] + + let subtitleSetExtensions = [ + ".[a-z]{2}.vtt", + ".[a-z]{2}.srt" + ] + + let setExtensions = [ + ".annotations.xml", + ".info.json", + ".description", + ".jpg" + ].concat(subtitleSetExtensions); + + let match = inExtensions(item.name, matchExtensions); + + if (match) { + let setFiles = findMatchingBasenames(item.name, match, Object.keys(allItems), setExtensions); + + let nonSubtitles = setFiles.filter((setItem) => { + return !(inExtensions(setItem, subtitleSetExtensions)); + }); + + if (nonSubtitles.length > 0) { + return setFiles; + } else { + // If we have either nothing or only subtitles, let the generic video file plugin handle it. + return []; + } + } else { + return []; + } +} \ No newline at end of file diff --git a/src/filesystem/list-directory.js b/src/filesystem/list-directory.js index cfa3d2b..bc3b887 100644 --- a/src/filesystem/list-directory.js +++ b/src/filesystem/list-directory.js @@ -5,6 +5,7 @@ const fs = Promise.promisifyAll(require("fs")); const path = require("path"); const rfr = require("rfr"); +const filesetPairer = rfr("lib/filesets"); const directorySorter = rfr("lib/sorting/directory")(); function statComplete(target) { @@ -77,6 +78,8 @@ module.exports = function(basePath, options = {}) { } }) }).then((entries) => { - return entries.sort(directorySorter); + return filesetPairer(entries, { + sorter: directorySorter + }); }); } \ No newline at end of file diff --git a/src/path/extension-matches.js b/src/path/extension-matches.js new file mode 100644 index 0000000..eaac882 --- /dev/null +++ b/src/path/extension-matches.js @@ -0,0 +1,6 @@ +'use strict'; + +module.exports = function(filename, extension) { + let matcher = new RegExp(extension.replace(/\./g, "\.") + "$"); + return matcher.test(filename); +} \ No newline at end of file diff --git a/src/path/in-extensions.js b/src/path/in-extensions.js new file mode 100644 index 0000000..338b850 --- /dev/null +++ b/src/path/in-extensions.js @@ -0,0 +1,16 @@ +'use strict'; + +const rfr = require("rfr"); +const extensionMatches = rfr("lib/path/extension-matches"); + +module.exports = function inExtensions(filename, extensions) { + let matches = extensions.filter((extension) => { + return extensionMatches(filename, extension); + }); + + if (matches.length > 0) { + return matches[0]; + } else { + return false; + } +} \ No newline at end of file diff --git a/src/path/subtract-extension.js b/src/path/subtract-extension.js new file mode 100644 index 0000000..e86f66e --- /dev/null +++ b/src/path/subtract-extension.js @@ -0,0 +1,6 @@ +'use strict'; + +module.exports = function(filename, extension) { + let matcher = new RegExp(extension.replace(/\./g, "\.") + "$"); + return filename.replace(matcher, ""); +} \ No newline at end of file