diff --git a/src/app.js b/src/app.js index e98e1e2d..83934a22 100755 --- a/src/app.js +++ b/src/app.js @@ -24,7 +24,7 @@ const { updateSceneSearch, updateMovieSearch } = require('./update-search'); const { scrapeActors, deleteActors, flushActors, flushProfiles, interpolateProfiles } = require('./actors'); const { flushEntities } = require('./entities'); const { deleteScenes, deleteMovies, flushScenes, flushMovies, flushBatches } = require('./releases'); -const { flushOrphanedMedia } = require('./media'); +const { flushOrphanedMedia, detachReleaseMedia, detachEntityReleaseMedia } = require('./media'); const { reassociateEntityReleaseTags, reassociateReleaseTags, reassociateOriginalTags } = require('./tags'); const getFileEntries = require('./utils/file-entries'); @@ -160,7 +160,8 @@ async function init() { } if (argv.flushNetworks || argv.flushChannels) { - await flushEntities(argv.flushNetworks, argv.flushChannels); + // inject flushOrphanedMedia to prevent circular dependency with entity media flush + await flushEntities(argv.flushNetworks, argv.flushChannels, flushOrphanedMedia); } if (argv.flushBatches) { @@ -203,6 +204,14 @@ async function init() { await flushOrphanedMedia(); } + if (argv.detachReleaseMedia) { + await detachReleaseMedia(argv.detachReleaseMedia); + } + + if (argv.detachNetworkMedia || argv.detachChannelMedia) { + await detachEntityReleaseMedia(argv.detachNetworkMedia, argv.detachChannelMedia); + } + if (argv.request) { const res = await http[argv.requestMethod](argv.request); diff --git a/src/argv.js b/src/argv.js index f191b6b2..0874c419 100755 --- a/src/argv.js +++ b/src/argv.js @@ -349,7 +349,32 @@ const { argv } = yargs describe: 'Remove files from storage when flushing media.', type: 'boolean', alias: 'flush-files', - default: true, + }) + .option('detach-channel-media', { + describe: 'Remove media files from channel scenes.', + type: 'array', + }) + .option('detach-network-media', { + describe: 'Remove media files from network scenes.', + type: 'array', + }) + .option('detach-release-media', { + describe: 'Remove media files from network scenes.', + type: 'array', + alias: ['detach-scene-media'], + }) + .option('detach-media-domains', { + describe: 'Only detach these types of media.', + type: 'array', + default: [ + 'posters', + 'photos', + 'caps', + 'trailers', + 'teasers', + 'covers', + ], + alias: ['detach-media'], }) .option('flush-channels', { describe: 'Delete all scenes and movies from channels.', diff --git a/src/entities.js b/src/entities.js index 34cf522a..788510d8 100755 --- a/src/entities.js +++ b/src/entities.js @@ -7,7 +7,6 @@ const logger = require('./logger')(__filename); const argv = require('./argv'); const knex = require('./knex'); const { deleteScenes, deleteMovies, deleteSeries } = require('./releases'); -const { flushOrphanedMedia } = require('./media'); const { resolveScraper, resolveLayoutScraper } = require('./scrapers/resolve'); const getRecursiveParameters = require('./utils/get-recursive-parameters'); @@ -454,7 +453,7 @@ async function fetchEntityReleaseIds(networkSlugs = [], channelSlugs = []) { }; } -async function flushEntities(networkSlugs = [], channelSlugs = []) { +async function flushEntities(networkSlugs = [], channelSlugs = [], flushOrphanedMedia) { const { sceneIds, movieIds, serieIds } = await fetchEntityReleaseIds(networkSlugs, channelSlugs); const entitySlugs = networkSlugs.concat(channelSlugs).join(', '); diff --git a/src/media.js b/src/media.js index 68579b36..79d3399c 100755 --- a/src/media.js +++ b/src/media.js @@ -26,6 +26,7 @@ const http = require('./utils/http'); const bulkInsert = require('./utils/bulk-insert'); const chunk = require('./utils/chunk'); const { get } = require('./utils/qu'); +const { fetchEntityReleaseIds } = require('./entities'); // const pipeline = util.promisify(stream.pipeline); const streamQueue = taskQueue(); @@ -1159,8 +1160,36 @@ async function flushOrphanedMedia(stage = 1) { } } +async function detachReleaseMedia(rawSceneIds) { + const sceneIds = rawSceneIds.map((sceneId) => Number(sceneId)).filter(Boolean); + + await argv.detachMediaDomains.reduce(async (chain, domain) => { + await chain; + + const mediaEntries = await knex(`releases_${domain}`).whereIn('release_id', sceneIds); + + await knex(`releases_${domain}`) + .whereIn('release_id', sceneIds) + .delete(); + + logger.info(`Removed ${mediaEntries.length} ${domain} from ${new Set(mediaEntries.map((mediaEntry) => mediaEntry.release_id)).size} scenes`); + }, Promise.resolve()); + + if (argv.flushOrphanedMedia !== false) { + await flushOrphanedMedia(); + } +} + +async function detachEntityReleaseMedia(networkSlugs = [], channelSlugs = []) { + const { sceneIds } = await fetchEntityReleaseIds(networkSlugs, channelSlugs); + + await detachReleaseMedia(sceneIds); +} + module.exports = { associateAvatars, associateReleaseMedia, flushOrphanedMedia, + detachReleaseMedia, + detachEntityReleaseMedia, };