Storing image dimensions and file size to database. Added new site Filthy Femdom to Kink.
|  | @ -26,8 +26,11 @@ exports.up = knex => Promise.resolve() | ||||||
|         table.string('mime'); |         table.string('mime'); | ||||||
| 
 | 
 | ||||||
|         table.string('hash'); |         table.string('hash'); | ||||||
|         table.string('type'); | 
 | ||||||
|         table.string('quality', 6); |         table.integer('size', 12); | ||||||
|  |         table.integer('quality', 6); | ||||||
|  |         table.integer('width', 6); | ||||||
|  |         table.integer('height', 6); | ||||||
|         table.float('entropy'); |         table.float('entropy'); | ||||||
| 
 | 
 | ||||||
|         table.text('comment'); |         table.text('comment'); | ||||||
|  |  | ||||||
| Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 31 KiB | 
| Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB | 
| Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB | 
| Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 49 KiB | 
| Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB | 
| Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 47 KiB | 
| Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 47 KiB | 
| Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 29 KiB | 
| Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 26 KiB | 
| Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 32 KiB | 
| Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.8 KiB | 
| After Width: | Height: | Size: 21 KiB | 
| Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 6.5 KiB | 
| Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 5.8 KiB | 
| Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 19 KiB | 
| Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 26 KiB | 
| Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 44 KiB | 
| Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 10 KiB | 
| Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 38 KiB | 
| Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 14 KiB | 
| Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 15 KiB | 
| Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 35 KiB | 
| Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 37 KiB | 
| Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 52 KiB | 
| Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 34 KiB | 
| Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 9.2 KiB | 
| Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 51 KiB | 
| Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 3.6 KiB | 
| Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.2 KiB | 
| Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 21 KiB | 
| Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 47 KiB | 
| Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 20 KiB | 
| Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 15 KiB | 
| Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 32 KiB | 
| Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 18 KiB | 
| Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 7.6 KiB | 
| Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 52 KiB | 
|  | @ -2169,7 +2169,7 @@ const sites = [ | ||||||
|         slug: 'boundgangbangs', |         slug: 'boundgangbangs', | ||||||
|         name: 'Bound Gangbangs', |         name: 'Bound Gangbangs', | ||||||
|         url: 'https://www.kink.com/channel/boundgangbangs', |         url: 'https://www.kink.com/channel/boundgangbangs', | ||||||
|         description: 'Poweless whores tied in bondage and stuffed with a cock in every hole. At BoundGangbangs women get surprise extreme gangbangs, blindfolds, deepthroat blowjobs, sex punishment, bondage, double penetration and interracial sex.', |         description: 'Powerless whores tied in bondage and stuffed with a cock in every hole. At BoundGangbangs women get surprise extreme gangbangs, blindfolds, deepthroat blowjobs, sex punishment, bondage, double penetration and interracial sex.', | ||||||
|         network: 'kink', |         network: 'kink', | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|  | @ -2231,6 +2231,14 @@ const sites = [ | ||||||
|         description: 'Gaping Anal Holes Are Stuffed & Stretched To The Max. Anal Fisting, Enemas & Rimming Has Never Tasted So Good. EverythingButt.com explores the extreme limits of FemDom lesbian anal. Watch asses get destroyed by brutal fistings, huge insertions, double anal & more!', |         description: 'Gaping Anal Holes Are Stuffed & Stretched To The Max. Anal Fisting, Enemas & Rimming Has Never Tasted So Good. EverythingButt.com explores the extreme limits of FemDom lesbian anal. Watch asses get destroyed by brutal fistings, huge insertions, double anal & more!', | ||||||
|         network: 'kink', |         network: 'kink', | ||||||
|     }, |     }, | ||||||
|  |     { | ||||||
|  |         slug: 'filthyfemdom', | ||||||
|  |         name: 'Filthy Femdom', | ||||||
|  |         url: 'https://www.kink.com/channel/filthyfemdom', | ||||||
|  |         description: 'Powerful women dominate your dirty dreams of sweet pain, seductive bondage, and sexual servitude.', | ||||||
|  |         tags: ['femdom'], | ||||||
|  |         network: 'kink', | ||||||
|  |     }, | ||||||
|     { |     { | ||||||
|         slug: 'familiestied', |         slug: 'familiestied', | ||||||
|         name: 'Families Tied', |         name: 'Families Tied', | ||||||
|  |  | ||||||
							
								
								
									
										41
									
								
								src/media.js
								
								
								
								
							
							
						
						|  | @ -62,11 +62,17 @@ function pickQuality(items) { | ||||||
|     return item || items[0]; |     return item || items[0]; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async function getEntropy(buffer) { | async function getMeta(buffer) { | ||||||
|     try { |     try { | ||||||
|         const { entropy } = await sharp(buffer).stats(); |         const { entropy } = await sharp(buffer).stats(); | ||||||
|  |         const { width, height, size } = await sharp(buffer).metadata(); | ||||||
| 
 | 
 | ||||||
|         return entropy; |         return { | ||||||
|  |             width, | ||||||
|  |             height, | ||||||
|  |             size, | ||||||
|  |             entropy, | ||||||
|  |         }; | ||||||
|     } catch (error) { |     } catch (error) { | ||||||
|         logger.warn(`Failed to retrieve image entropy, using 7.5: ${error.message}`); |         logger.warn(`Failed to retrieve image entropy, using 7.5: ${error.message}`); | ||||||
| 
 | 
 | ||||||
|  | @ -125,7 +131,7 @@ async function fetchItem(source, index, existingItemsBySource, domain, role, att | ||||||
|             const mimetype = mime.getType(pathname); |             const mimetype = mime.getType(pathname); | ||||||
|             const extension = mime.getExtension(mimetype); |             const extension = mime.getExtension(mimetype); | ||||||
|             const hash = getHash(res.body); |             const hash = getHash(res.body); | ||||||
|             const entropy = /image/.test(mimetype) ? await getEntropy(res.body) : null; |             const { entropy, size, width, height } = /image/.test(mimetype) ? await getMeta(res.body) : {}; | ||||||
| 
 | 
 | ||||||
|             logger.verbose(`Fetched media item from ${source.src || source}`); |             logger.verbose(`Fetched media item from ${source.src || source}`); | ||||||
| 
 | 
 | ||||||
|  | @ -135,6 +141,9 @@ async function fetchItem(source, index, existingItemsBySource, domain, role, att | ||||||
|                 extension, |                 extension, | ||||||
|                 hash, |                 hash, | ||||||
|                 entropy, |                 entropy, | ||||||
|  |                 size, | ||||||
|  |                 width, | ||||||
|  |                 height, | ||||||
|                 quality: source.quality || null, |                 quality: source.quality || null, | ||||||
|                 source: originalSource?.src || originalSource || source.src || source, |                 source: originalSource?.src || originalSource || source.src || source, | ||||||
|                 scraper: source.scraper, |                 scraper: source.scraper, | ||||||
|  | @ -192,10 +201,13 @@ async function saveItems(items, domain, role) { | ||||||
|                     mimetype: item.mimetype, |                     mimetype: item.mimetype, | ||||||
|                     extension: item.extension, |                     extension: item.extension, | ||||||
|                     hash: item.hash, |                     hash: item.hash, | ||||||
|  |                     size: item.size, | ||||||
|  |                     width: item.width, | ||||||
|  |                     height: item.height, | ||||||
|  |                     quality: item.quality, | ||||||
|                     entropy: item.entropy, |                     entropy: item.entropy, | ||||||
|                     scraper: item.scraper, |                     scraper: item.scraper, | ||||||
|                     copyright: item.copyright, |                     copyright: item.copyright, | ||||||
|                     quality: item.quality, |  | ||||||
|                     source: item.source, |                     source: item.source, | ||||||
|                 }; |                 }; | ||||||
|             } |             } | ||||||
|  | @ -207,9 +219,12 @@ async function saveItems(items, domain, role) { | ||||||
|                 mimetype: item.mimetype, |                 mimetype: item.mimetype, | ||||||
|                 extension: item.extension, |                 extension: item.extension, | ||||||
|                 hash: item.hash, |                 hash: item.hash, | ||||||
|  |                 size: item.size, | ||||||
|  |                 width: item.width, | ||||||
|  |                 height: item.height, | ||||||
|  |                 quality: item.quality, | ||||||
|                 entropy: item.entropy, |                 entropy: item.entropy, | ||||||
|                 scraper: item.scraper, |                 scraper: item.scraper, | ||||||
|                 quality: item.quality, |  | ||||||
|                 copyright: item.copyright, |                 copyright: item.copyright, | ||||||
|                 source: item.source, |                 source: item.source, | ||||||
|             }; |             }; | ||||||
|  | @ -226,6 +241,10 @@ function curateItemEntries(items) { | ||||||
|         thumbnail: item.thumbpath, |         thumbnail: item.thumbpath, | ||||||
|         mime: item.mimetype, |         mime: item.mimetype, | ||||||
|         hash: item.hash, |         hash: item.hash, | ||||||
|  |         size: item.size, | ||||||
|  |         width: item.width, | ||||||
|  |         height: item.height, | ||||||
|  |         quality: item.quality, | ||||||
|         entropy: item.entropy, |         entropy: item.entropy, | ||||||
|         source: item.source, |         source: item.source, | ||||||
|         scraper: item.scraper, |         scraper: item.scraper, | ||||||
|  | @ -317,17 +336,21 @@ function associateTargetMedia(targetId, sources, mediaBySource, domain, role, pr | ||||||
|     if (!sources) return { [role]: null, [primaryRole]: null }; |     if (!sources) return { [role]: null, [primaryRole]: null }; | ||||||
| 
 | 
 | ||||||
|     const mediaIds = sources |     const mediaIds = sources | ||||||
|         .filter(Boolean) |  | ||||||
|         .map((source) => { |         .map((source) => { | ||||||
|  |             if (!source) return null; | ||||||
|  | 
 | ||||||
|             const mediaItem = Array.isArray(source) |             const mediaItem = Array.isArray(source) | ||||||
|                 ? source.reduce((acc, sourceX) => acc || mediaBySource[sourceX.src || sourceX], null) |                 ? source.reduce((acc, sourceX) => acc || mediaBySource[sourceX.src || sourceX], null) | ||||||
|                 : mediaBySource[source.src || source]; |                 : mediaBySource[source.src || source]; | ||||||
| 
 | 
 | ||||||
|             // return mediaItem && { [`${domain}_id`]: targetId, media_id: mediaItem.id };
 |             // return mediaItem && { [`${domain}_id`]: targetId, media_id: mediaItem.id };
 | ||||||
|             return mediaItem && mediaItem.id; |             return mediaItem; | ||||||
|         }); |         }) | ||||||
|  |         .filter(Boolean) | ||||||
|  |         // .sort((mediaItemA, mediaItemB) => mediaItemB.height - mediaItemA.height) // prefer high res images for primary item
 | ||||||
|  |         .map(mediaItem => mediaItem.id); | ||||||
| 
 | 
 | ||||||
|     const uniqueMediaIds = Array.from(new Set(mediaIds.filter(Boolean))); |     const uniqueMediaIds = Array.from(new Set(mediaIds)); | ||||||
|     const associations = uniqueMediaIds.map(mediaId => ({ [`${domain}_id`]: targetId, media_id: mediaId })); |     const associations = uniqueMediaIds.map(mediaId => ({ [`${domain}_id`]: targetId, media_id: mediaId })); | ||||||
| 
 | 
 | ||||||
|     logger.silly(`Associating ${associations.length} ${role}s to ${domain} ${targetId}`); |     logger.silly(`Associating ${associations.length} ${role}s to ${domain} ${targetId}`); | ||||||
|  |  | ||||||