vidup 1.0.6 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/cli.js +4 -4
- package/dist/lib/index.js +1 -1
- package/dist/sync-Cw-C7k7G.js +7 -0
- package/license.txt +1 -1
- package/package.json +76 -37
- package/readme.md +9 -9
- package/dist/sync-fEVhgE0O.js +0 -7
package/dist/bin/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var M=Object.defineProperty;var a=(e,t)=>M(e,"name",{value:t,configurable:!0});import I from"node:path";import _ from"node:os";import O from"yargs";import{hideBin as S}from"yargs/helpers";import{l as h,s as $,a as E}from"../sync-
|
|
3
|
-
`)}else{for(const
|
|
4
|
-
`);for(const
|
|
5
|
-
`)}h.info(`Synchronized video in ${
|
|
2
|
+
var M=Object.defineProperty;var a=(e,t)=>M(e,"name",{value:t,configurable:!0});import I from"node:path";import _ from"node:os";import O from"yargs";import{hideBin as S}from"yargs/helpers";import{l as h,s as $,a as E}from"../sync-Cw-C7k7G.js";import"bunnycdn-stream";import"hasha";import"node:fs";import"node:fs/promises";import"node:url";import"ora";import"execa";import"ffmpeg-static";const D=a(e=>Number.isFinite(e)?e:0,"toZeroIfInfinity");function F(e){return{days:Math.trunc(e/864e5),hours:Math.trunc(e/36e5%24),minutes:Math.trunc(e/6e4%60),seconds:Math.trunc(e/1e3%60),milliseconds:Math.trunc(e%1e3),microseconds:Math.trunc(D(e*1e3)%1e3),nanoseconds:Math.trunc(D(e*1e6)%1e3)}}a(F,"parseNumber");function x(e){return{days:e/86400000n,hours:e/3600000n%24n,minutes:e/60000n%60n,seconds:e/1000n%60n,milliseconds:e%1000n,microseconds:0n,nanoseconds:0n}}a(x,"parseBigint");function k(e){switch(typeof e){case"number":{if(Number.isFinite(e))return F(e);break}case"bigint":return x(e)}throw new TypeError("Expected a finite number or bigint")}a(k,"parseMilliseconds");const B=a(e=>e===0||e===0n,"isZero"),C=a((e,t)=>t===1||t===1n?e:`${e}s`,"pluralize"),T=1e-7,z=24n*60n*60n*1000n;function A(e,t){const u=typeof e=="bigint";if(!u&&!Number.isFinite(e))throw new TypeError("Expected a finite number or bigint");t={...t};const f=e<0?"-":"";e=e<0?-e:e,t.colonNotation&&(t.compact=!1,t.formatSubMilliseconds=!1,t.separateMilliseconds=!1,t.verbose=!1),t.compact&&(t.unitCount=1,t.secondsDecimalDigits=0,t.millisecondsDecimalDigits=0);let d=[];const b=a((i,n)=>{const c=Math.floor(i*10**n+T);return(Math.round(c)/10**n).toFixed(n)},"floorDecimals"),r=a((i,n,c,o)=>{if(!((d.length===0||!t.colonNotation)&&B(i)&&!(t.colonNotation&&c==="m"))){if(o??=String(i),t.colonNotation){const m=o.includes(".")?o.split(".")[0].length:o.length,y=d.length>0?2:1;o="0".repeat(Math.max(0,y-m))+o}else o+=t.verbose?" "+C(n,i):c;d.push(o)}},"add"),s=k(e),l=BigInt(s.days);if(t.hideYearAndDays?r(BigInt(l)*24n+BigInt(s.hours),"hour","h"):(t.hideYear?r(l,"day","d"):(r(l/365n,"year","y"),r(l%365n,"day","d")),r(Number(s.hours),"hour","h")),r(Number(s.minutes),"minute","m"),!t.hideSeconds)if(t.separateMilliseconds||t.formatSubMilliseconds||!t.colonNotation&&e<1e3){const i=Number(s.seconds),n=Number(s.milliseconds),c=Number(s.microseconds),o=Number(s.nanoseconds);if(r(i,"second","s"),t.formatSubMilliseconds)r(n,"millisecond","ms"),r(c,"microsecond","\xB5s"),r(o,"nanosecond","ns");else{const m=n+c/1e3+o/1e6,y=typeof t.millisecondsDecimalDigits=="number"?t.millisecondsDecimalDigits:0,v=m>=1?Math.round(m):Math.ceil(m),g=y?m.toFixed(y):v;r(Number.parseFloat(g),"millisecond","ms",g)}}else{const i=(u?Number(e%z):e)/1e3%60,n=typeof t.secondsDecimalDigits=="number"?t.secondsDecimalDigits:1,c=b(i,n),o=t.keepDecimalsOnWholeSeconds?c:c.replace(/\.0+$/,"");r(Number.parseFloat(o),"second","s",o)}if(d.length===0)return f+"0"+(t.verbose?" milliseconds":"ms");const p=t.colonNotation?":":" ";return typeof t.unitCount=="number"&&(d=d.slice(0,Math.max(t.unitCount,1))),f+d.join(p)}a(A,"prettyMilliseconds");const N=_.homedir();function P(e){if(typeof e!="string")throw new TypeError(`Expected a string, got ${typeof e}`);return N?e.replace(/^~(?=$|\/|\\)/,N):e}a(P,"untildify");var V="1.0.8";const L=performance.now(),w=O(S(process.argv));await w.scriptName("vidup").usage("$0 [command]","Run a vidup command.").command(["$0 <directory> [options]","sync <directory> [options]"],"Synchronize a remote video streaming service to mirror the contents of a local directory. Warning: This command will irrevocably delete remote videos that are not present in the local directory.",e=>e.positional("directory",{default:void 0,demandOption:!0,describe:"The path to the local directory of video files to sync.",type:"string"}).option("service",{alias:"s",choices:["bunny","mux","cloudflare"],demandOption:!0,describe:"Streaming service / platform to sync local video files to. Only the Bunny.net streaming CDN is supported at this time.",type:"string"}).option("key",{default:void 0,demandOption:!0,describe:"Streaming service API access key.",type:"string"}).option("library",{default:void 0,demandOption:!0,describe:"Streaming service library ID.",type:"string"}).option("strip-metadata",{default:!1,describe:"Remove all metadata from the video files before uploading them to the streaming service. Warning: This will modify local videos in-place.",type:"boolean"}).option("dry-run",{alias:"d",default:!1,describe:"Perform a dry run without making any changes. Useful for testing and debugging. Pairs well with the `--json` command.",type:"boolean"}).option("json",{default:!1,describe:"Output the sync report as JSON.",type:"boolean"}).option("verbose",{default:!1,describe:"Enable verbose logging. All verbose logs and prefixed with their log level and are printed to `stderr` for ease of redirection.",type:"boolean"}).check(t=>{if(t.service==="bunny")return t;throw new Error(`Sorry, vidup doesn't yet implement synchronization support the ${t.service} streaming service. Only Bunny.net is supported at the moment. If you'd like to see support for another service, please open an issue or send a PR on GitHub: https://github.com/kitschpatrol/vidup/issues`)}),async({directory:e,dryRun:t,json:u,key:f,library:d,service:b,stripMetadata:r,verbose:s})=>{h.verbose=s;const l=I.resolve(P(e));h.info("Starting video synchronization..."),t&&h.warn("Dry run enabled, not making any changes");const p=r?await $(l,{dryRun:t,verbose:s}):[],i=await E(l,{credentials:{key:f,library:d},dryRun:t,service:b,verbose:s});if(u){for(const n of i)n.stripMetadata=n.localFile!==void 0&&p.includes(n.localFile);process.stdout.write(JSON.stringify(i,void 0,2)),process.stdout.write(`
|
|
3
|
+
`)}else{for(const n of p)process.stdout.write(`${t?"Found":"Stripped"} metadata: "${n}"
|
|
4
|
+
`);for(const n of i)process.stdout.write(`${n.action}: "${n.localFile}" Remote GUID: "${n.remoteId}"
|
|
5
|
+
`)}h.info(`Synchronized video in ${A(performance.now()-L)}`),process.exitCode=0}).demandCommand(1).alias("h","help").version(V).alias("v","version").help().wrap(process.stdout.isTTY?Math.min(120,w.terminalWidth()):0).parse();
|
package/dist/lib/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{l as n,s as y,a as l}from"../sync-
|
|
1
|
+
import{l as n,s as y,a as l}from"../sync-Cw-C7k7G.js";import"bunnycdn-stream";import"hasha";import"node:fs";import"node:fs/promises";import"node:path";import"node:url";import"ora";import"execa";import"ffmpeg-static";export{n as log,y as stripVideoMetadataInDirectory,l as syncVideoInDirectory};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
var Z=Object.defineProperty;var i=(t,e)=>Z(t,"name",{value:e,configurable:!0});import{BunnyCdnStream as ee}from"bunnycdn-stream";import{hash as te}from"hasha";import{createReadStream as U}from"node:fs";import k,{writeFile as ne,rename as oe,readFile as re}from"node:fs/promises";import w,{join as ie,dirname as se,basename as ae}from"node:path";import{fileURLToPath as le}from"node:url";import{oraPromise as ce}from"ora";import{execa as H}from"execa";import j from"ffmpeg-static";const M=10,G=i((t=0)=>e=>`\x1B[${e+t}m`,"wrapAnsi16"),L=i((t=0)=>e=>`\x1B[${38+t};5;${e}m`,"wrapAnsi256"),W=i((t=0)=>(e,n,o)=>`\x1B[${38+t};2;${e};${n};${o}m`,"wrapAnsi16m"),c={modifier:{reset:[0,0],bold:[1,22],dim:[2,22],italic:[3,23],underline:[4,24],overline:[53,55],inverse:[7,27],hidden:[8,28],strikethrough:[9,29]},color:{black:[30,39],red:[31,39],green:[32,39],yellow:[33,39],blue:[34,39],magenta:[35,39],cyan:[36,39],white:[37,39],blackBright:[90,39],gray:[90,39],grey:[90,39],redBright:[91,39],greenBright:[92,39],yellowBright:[93,39],blueBright:[94,39],magentaBright:[95,39],cyanBright:[96,39],whiteBright:[97,39]},bgColor:{bgBlack:[40,49],bgRed:[41,49],bgGreen:[42,49],bgYellow:[43,49],bgBlue:[44,49],bgMagenta:[45,49],bgCyan:[46,49],bgWhite:[47,49],bgBlackBright:[100,49],bgGray:[100,49],bgGrey:[100,49],bgRedBright:[101,49],bgGreenBright:[102,49],bgYellowBright:[103,49],bgBlueBright:[104,49],bgMagentaBright:[105,49],bgCyanBright:[106,49],bgWhiteBright:[107,49]}};Object.keys(c.modifier);const de=Object.keys(c.color),fe=Object.keys(c.bgColor);[...de,...fe];function ue(){const t=new Map;for(const[e,n]of Object.entries(c)){for(const[o,r]of Object.entries(n))c[o]={open:`\x1B[${r[0]}m`,close:`\x1B[${r[1]}m`},n[o]=c[o],t.set(r[0],r[1]);Object.defineProperty(c,e,{value:n,enumerable:!1})}return Object.defineProperty(c,"codes",{value:t,enumerable:!1}),c.color.close="\x1B[39m",c.bgColor.close="\x1B[49m",c.color.ansi=G(),c.color.ansi256=L(),c.color.ansi16m=W(),c.bgColor.ansi=G(M),c.bgColor.ansi256=L(M),c.bgColor.ansi16m=W(M),Object.defineProperties(c,{rgbToAnsi256:{value(e,n,o){return e===n&&n===o?e<8?16:e>248?231:Math.round((e-8)/247*24)+232:16+36*Math.round(e/255*5)+6*Math.round(n/255*5)+Math.round(o/255*5)},enumerable:!1},hexToRgb:{value(e){const n=/[a-f\d]{6}|[a-f\d]{3}/i.exec(e.toString(16));if(!n)return[0,0,0];let[o]=n;o.length===3&&(o=[...o].map(l=>l+l).join(""));const r=Number.parseInt(o,16);return[r>>16&255,r>>8&255,r&255]},enumerable:!1},hexToAnsi256:{value:i(e=>c.rgbToAnsi256(...c.hexToRgb(e)),"value"),enumerable:!1},ansi256ToAnsi:{value(e){if(e<8)return 30+e;if(e<16)return 90+(e-8);let n,o,r;if(e>=232)n=((e-232)*10+8)/255,o=n,r=n;else{e-=16;const m=e%36;n=Math.floor(e/36)/5,o=Math.floor(m/6)/5,r=m%6/5}const l=Math.max(n,o,r)*2;if(l===0)return 30;let u=30+(Math.round(r)<<2|Math.round(o)<<1|Math.round(n));return l===2&&(u+=60),u},enumerable:!1},rgbToAnsi:{value:i((e,n,o)=>c.ansi256ToAnsi(c.rgbToAnsi256(e,n,o)),"value"),enumerable:!1},hexToAnsi:{value:i(e=>c.ansi256ToAnsi(c.hexToAnsi256(e)),"value"),enumerable:!1}}),c}i(ue,"assembleStyles");const g=ue(),Y=(()=>{if(!("navigator"in globalThis))return 0;if(globalThis.navigator.userAgentData){const t=navigator.userAgentData.brands.find(({brand:e})=>e==="Chromium");if(t&&t.version>93)return 3}return/\b(Chrome|Chromium)\//.test(globalThis.navigator.userAgent)?1:0})(),J=Y!==0&&{level:Y},he={stdout:J,stderr:J};function me(t,e,n){let o=t.indexOf(e);if(o===-1)return t;const r=e.length;let l=0,u="";do u+=t.slice(l,o)+e+n,l=o+r,o=t.indexOf(e,l);while(o!==-1);return u+=t.slice(l),u}i(me,"stringReplaceAll");function ge(t,e,n,o){let r=0,l="";do{const u=t[o-1]==="\r";l+=t.slice(r,u?o-1:o)+e+(u?`\r
|
|
2
|
+
`:`
|
|
3
|
+
`)+n,r=o+1,o=t.indexOf(`
|
|
4
|
+
`,r)}while(o!==-1);return l+=t.slice(r),l}i(ge,"stringEncaseCRLFWithFirstIndex");const{stdout:K,stderr:_}=he,V=Symbol("GENERATOR"),$=Symbol("STYLER"),T=Symbol("IS_EMPTY"),z=["ansi","ansi","ansi256","ansi16m"],F=Object.create(null),pe=i((t,e={})=>{if(e.level&&!(Number.isInteger(e.level)&&e.level>=0&&e.level<=3))throw new Error("The `level` option should be an integer from 0 to 3");const n=K?K.level:0;t.level=e.level===void 0?n:e.level},"applyOptions"),we=i(t=>{const e=i((...n)=>n.join(" "),"chalk");return pe(e,t),Object.setPrototypeOf(e,B.prototype),e},"chalkFactory");function B(t){return we(t)}i(B,"createChalk"),Object.setPrototypeOf(B.prototype,Function.prototype);for(const[t,e]of Object.entries(g))F[t]={get(){const n=C(this,D(e.open,e.close,this[$]),this[T]);return Object.defineProperty(this,t,{value:n}),n}};F.visible={get(){const t=C(this,this[$],!0);return Object.defineProperty(this,"visible",{value:t}),t}};const N=i((t,e,n,...o)=>t==="rgb"?e==="ansi16m"?g[n].ansi16m(...o):e==="ansi256"?g[n].ansi256(g.rgbToAnsi256(...o)):g[n].ansi(g.rgbToAnsi(...o)):t==="hex"?N("rgb",e,n,...g.hexToRgb(...o)):g[n][t](...o),"getModelAnsi"),be=["rgb","hex","ansi256"];for(const t of be){F[t]={get(){const{level:n}=this;return function(...o){const r=D(N(t,z[n],"color",...o),g.color.close,this[$]);return C(this,r,this[T])}}};const e="bg"+t[0].toUpperCase()+t.slice(1);F[e]={get(){const{level:n}=this;return function(...o){const r=D(N(t,z[n],"bgColor",...o),g.bgColor.close,this[$]);return C(this,r,this[T])}}}}const ye=Object.defineProperties(()=>{},{...F,level:{enumerable:!0,get(){return this[V].level},set(t){this[V].level=t}}}),D=i((t,e,n)=>{let o,r;return n===void 0?(o=t,r=e):(o=n.openAll+t,r=e+n.closeAll),{open:t,close:e,openAll:o,closeAll:r,parent:n}},"createStyler"),C=i((t,e,n)=>{const o=i((...r)=>ve(o,r.length===1?""+r[0]:r.join(" ")),"builder");return Object.setPrototypeOf(o,ye),o[V]=t,o[$]=e,o[T]=n,o},"createBuilder"),ve=i((t,e)=>{if(t.level<=0||!e)return t[T]?"":e;let n=t[$];if(n===void 0)return e;const{openAll:o,closeAll:r}=n;if(e.includes("\x1B"))for(;n!==void 0;)e=me(e,n.close,n.open),n=n.parent;const l=e.indexOf(`
|
|
5
|
+
`);return l!==-1&&(e=ge(e,r,o,l)),o+e+r},"applyStyle");Object.defineProperties(B.prototype,F);const p=B();B({level:_?_.level:0});const q=process.versions.node!==void 0,d={verbose:!1,log(...t){if(!this.verbose)return;const e=p.gray("[Log]");q?console.warn(e,...t):console.log(e,...t)},logPrefixed(t,...e){this.info(p.blue(`[${t}]`),...e)},info(...t){if(!this.verbose)return;const e=p.green("[Info]");q?console.warn(e,...t):console.info(e,...t)},infoPrefixed(t,...e){this.info(p.blue(`[${t}]`),...e)},async infoSpin(t,e){return ce(t,{prefixText:p.green("[Info]"),stream:process.stderr,text:e})},warn(...t){console.warn(p.yellow("[Warning]"),...t)},warnPrefixed(t,...e){this.warn(p.blue(`[${t}]`),...e)},error(...t){console.error(p.red("[Error]"),...t)},errorPrefixed(t,...e){this.error(p.blue(`[${t}]`),...e)}};async function Q(t){return(await k.readdir(t)).filter(o=>{const r=w.extname(o);return[".mp4",".mov",".avi",".mkv"].includes(r)}).map(o=>w.join(t,o))}i(Q,"getVideosInDirectory");async function $e(t){if(j===null)throw new Error("ffmpeg-static failed to locate ffmpeg binary");try{const{stderr:e,stdout:n}=await H(j,["-i",t,"-f","ffmetadata","-"]);return n.includes("creation_time")||e.includes("creation_time")}catch(e){throw new Error(`Failed to check metadata for: ${t}:
|
|
6
|
+
${String(e)}`)}}i($e,"videoHasMetadata");async function Fe(t){if(j===null)throw new Error("ffmpeg-static failed to locate ffmpeg binary");try{const e=w.extname(t).slice(1),n=`${t.slice(0,Math.max(0,t.lastIndexOf(".")))}.temp.${e}`;await H(j,["-i",t,"-map_metadata","-1","-c:v","copy","-c:a","copy",n]),await k.rename(n,t)}catch(e){throw new Error(`Failed to strip metadata from: ${t}:
|
|
7
|
+
${String(e)}`)}}i(Fe,"stripVideoMetadata");async function xe(t,e={}){const{dryRun:n=!1,verbose:o=!1}=e,r=await Q(t);return Se(r,{dryRun:n,verbose:o})}i(xe,"stripVideoMetadataInDirectory");async function Se(t,e={}){const{dryRun:n=!1,verbose:o=!1}=e,r=d.verbose;d.verbose=o;const l=[];for(const u of t)await $e(u)&&l.push(u);if(!n){d.info(`Found ${l.length} videos with metadata to strip`);for(const u of l)console.log(`Stripping metadata from: ${u}`),await Fe(u)}return d.verbose=r,l}i(Se,"stripVideoMetadataInFiles");function Ee(t){const e=t instanceof URL?le(t):t.toString();return ie(se(e),`.${ae(e)}.tmp`)}i(Ee,"getTempFilename");async function Oe(t,e,n){for(let o=0;o<e;o++)try{return await t()}catch(r){if(o<e-1)await new Promise(l=>setTimeout(l,n));else throw r}}i(Oe,"retryAsyncOperation");class Ae{static{i(this,"Writer")}#e;#t;#n=!1;#r=null;#i=null;#s=null;#o=null;#a(e){return this.#o=e,this.#s||=new Promise((n,o)=>{this.#i=[n,o]}),new Promise((n,o)=>{this.#s?.then(n).catch(o)})}async#l(e){this.#n=!0;try{await ne(this.#t,e,"utf-8"),await Oe(async()=>{await oe(this.#t,this.#e)},10,100),this.#r?.[0]()}catch(n){throw n instanceof Error&&this.#r?.[1](n),n}finally{if(this.#n=!1,this.#r=this.#i,this.#i=this.#s=null,this.#o!==null){const n=this.#o;this.#o=null,await this.write(n)}}}constructor(e){this.#e=e,this.#t=Ee(e)}async write(e){return this.#n?this.#a(e):this.#l(e)}}class Te{static{i(this,"TextFile")}#e;#t;constructor(e){this.#e=e,this.#t=new Ae(e)}async read(){let e;try{e=await re(this.#e,"utf-8")}catch(n){if(n.code==="ENOENT")return null;throw n}return e}write(e){return this.#t.write(e)}}class Be{static{i(this,"DataFile")}#e;#t;#n;constructor(e,{parse:n,stringify:o}){this.#e=new Te(e),this.#t=n,this.#n=o}async read(){const e=await this.#e.read();return e===null?null:this.#t(e)}write(e){return this.#e.write(this.#n(e))}}class Pe extends Be{static{i(this,"JSONFile")}constructor(e){super(e,{parse:JSON.parse,stringify:i(n=>JSON.stringify(n,null,2),"stringify")})}}class Re{static{i(this,"Memory")}#e=null;read(){return Promise.resolve(this.#e)}write(e){return this.#e=e,Promise.resolve()}}function je(t,e){if(t===void 0)throw new Error("lowdb: missing adapter");if(e===void 0)throw new Error("lowdb: missing default data")}i(je,"checkArgs");class Ce{static{i(this,"Low")}adapter;data;constructor(e,n){je(e,n),this.adapter=e,this.data=n}async read(){const e=await this.adapter.read();e&&(this.data=e)}async write(){this.data&&await this.adapter.write(this.data)}async update(e){e(this.data),await this.write()}}async function Ie(t,e){const n=process.env.NODE_ENV==="test"?new Re:new Pe(t),o=new Ce(n,e);return await o.read(),o}i(Ie,"JSONFilePreset");async function Me(t,e){const{credentials:n,dryRun:o=!1,service:r,verbose:l=!1}=e,u=d.verbose;d.verbose=l;const m=[],P=await Q(t);d.info(`Found ${P.length} video files in directory "${t}"`);const f=await Ie(w.join(t,".vidup-state.json"),{syncState:[]});f.data.syncState=f.data.syncState.filter(s=>P.includes(w.join(t,s.filename)));for(const s of P){const a=w.basename(s),h=await te(s,{algorithm:"sha1"}),b=f.data.syncState.find(y=>y.filename===a);b?b.localHash=h:f.data.syncState.push({filename:a,localHash:h,remoteHash:{}})}if(o||await f.write(),r!=="bunny")throw new Error(`Streaming service not yet implemented: ${r}`);const x=new ee({apiKey:n.key,videoLibrary:n.library}),S=await x.listAllVideos();d.info(`Found ${S.length} video files on ${r} remote streaming service`);const X=S.filter(s=>{const a=f.data.syncState.find(h=>h.filename===s.title);return a!==void 0&&a.localHash===a.remoteHash[r]});for(const s of X)m.push({action:"None",localFile:s.title,remoteId:s.guid});const E=f.data.syncState.filter(s=>!S.some(a=>a.title===s.filename));for(const s of E)m.push({action:"Create",localFile:s.filename,remoteId:"Not yet uploaded (Dry run)"});const O=S.filter(s=>{const a=f.data.syncState.find(h=>h.filename===s.title);return a!==void 0&&a.localHash!==a.remoteHash[r]});for(const s of O)m.push({action:"Update",localFile:s.title,remoteId:"Not yet uploaded (Dry run)"});const R=S.filter(s=>!f.data.syncState.some(a=>a.filename===s.title));for(const s of R)m.push({action:"Delete",localFile:s.title,remoteId:s.guid});if(!o){f.data.lastRun=new Date,await f.write(),d.info("Synchronizing...");for(const[s,a]of R.entries()){if(s===0&&d.info(`Deleting ${R.length} remote videos...`),!(await d.infoSpin(x.deleteVideo(a.guid),`Deleting remote video ${s+1}/${R.length}: ${a.title}`)).success)throw new Error(`Failed to delete remote video: ${a.title}`);f.data.lastUpdate=new Date,await f.write()}for(const[s,a]of E.entries()){s===0&&d.info(`Uploading ${E.length} new local videos...`);const h=U(w.join(t,a.filename)),b=await d.infoSpin(x.createAndUploadVideo(h,{title:a.filename}),`Uploading new video ${s+1}/${E.length}: ${a.filename}`);d.info(`Remote video ${s+1}/${E.length} created with GUID: ${b.guid}`);const y=f.data.syncState.find(v=>v.filename===a.filename);if(y===void 0)throw new Error(`Failed to find state entry for: ${a.filename}`);y.remoteHash[r]=a.localHash,f.data.lastUpdate=new Date,await f.write();const A=m.find(v=>v.localFile===a.filename);if(A===void 0)throw new Error(`Failed to find report entry for: ${a.filename}`);A.remoteId=b.guid}for(const[s,a]of O.entries()){s===0&&(d.info(`Updating ${O.length} remote videos...`),d.warn("Bunny does not currently support updating videos in-place, so vidup will delete and re-uploading instead... this will change the video's GUID!"));const h=f.data.syncState.find(I=>I.filename===a.title);if(h===void 0)throw new Error(`Failed to find state entry for: ${a.title}`);if(!(await x.deleteVideo(a.guid)).success)throw new Error(`Failed to delete remote video before updating: ${a.title}`);const y=U(w.join(t,h.filename)),A=await d.infoSpin(x.createAndUploadVideo(y,{title:h.filename}),`Updating remote video ${s+1}/${O.length}: ${a.title}`);d.info(`Updated remote video ${s+1}/${O.length} created with GUID: ${A.guid}`),h.remoteHash[r]=h.localHash,f.data.lastUpdate=new Date,await f.write();const v=m.find(I=>I.localFile===h.filename);if(v===void 0)throw new Error(`Failed to find report entry for: ${h.filename}`);v.remoteId=A.guid}d.info(`All ${P.length} videos are now in sync!`)}return d.verbose=u,m}i(Me,"syncVideoInDirectory");export{Me as a,d as l,xe as s};
|
package/license.txt
CHANGED
package/package.json
CHANGED
|
@@ -1,75 +1,114 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vidup",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"type": "module",
|
|
3
|
+
"version": "1.0.8",
|
|
5
4
|
"description": "Synchronize a local directory of video files to remote streaming services.",
|
|
6
|
-
"
|
|
5
|
+
"keywords": [
|
|
6
|
+
"bunnycdn-stream",
|
|
7
|
+
"bunnycdn",
|
|
8
|
+
"cli",
|
|
9
|
+
"npm-package",
|
|
10
|
+
"sync",
|
|
11
|
+
"upload",
|
|
12
|
+
"video"
|
|
13
|
+
],
|
|
7
14
|
"homepage": "https://github.com/kitschpatrol/vidup",
|
|
8
15
|
"bugs": "https://github.com/kitschpatrol/vidup/issues",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/github:kitschpatrol/vidup"
|
|
19
|
+
},
|
|
20
|
+
"license": "MIT",
|
|
9
21
|
"author": {
|
|
10
22
|
"name": "Eric Mika",
|
|
11
23
|
"email": "eric@ericmika.com",
|
|
12
24
|
"url": "https://ericmika.com"
|
|
13
25
|
},
|
|
14
|
-
"
|
|
15
|
-
"engines": {
|
|
16
|
-
"node": ">=18.0.0",
|
|
17
|
-
"pnpm": ">=9.0.0"
|
|
18
|
-
},
|
|
19
|
-
"bin": {
|
|
20
|
-
"vidup": "dist/bin/cli.js"
|
|
21
|
-
},
|
|
22
|
-
"main": "./dist/lib/index.js",
|
|
23
|
-
"module": "./dist/lib/index.js",
|
|
26
|
+
"type": "module",
|
|
24
27
|
"exports": {
|
|
25
28
|
".": {
|
|
26
29
|
"import": "./dist/lib/index.js",
|
|
27
30
|
"types": "./dist/lib/index.d.ts"
|
|
28
31
|
}
|
|
29
32
|
},
|
|
33
|
+
"main": "./dist/lib/index.js",
|
|
34
|
+
"module": "./dist/lib/index.js",
|
|
30
35
|
"types": "./dist/lib/index.d.ts",
|
|
36
|
+
"bin": {
|
|
37
|
+
"vidup": "dist/bin/cli.js"
|
|
38
|
+
},
|
|
31
39
|
"files": [
|
|
32
40
|
"dist/*"
|
|
33
41
|
],
|
|
34
|
-
"keywords": [
|
|
35
|
-
"bunnycdn-stream",
|
|
36
|
-
"bunnycdn",
|
|
37
|
-
"cli",
|
|
38
|
-
"npm-package",
|
|
39
|
-
"sync",
|
|
40
|
-
"upload",
|
|
41
|
-
"video"
|
|
42
|
-
],
|
|
43
42
|
"dependencies": {
|
|
44
|
-
"@types/fs-extra": "^11.0.4",
|
|
45
43
|
"@types/yargs": "^17.0.33",
|
|
46
|
-
"bunnycdn-stream": "^2.4.
|
|
47
|
-
"execa": "^9.
|
|
44
|
+
"bunnycdn-stream": "^2.4.1",
|
|
45
|
+
"execa": "^9.5.2",
|
|
48
46
|
"ffmpeg-static": "^5.2.0",
|
|
49
|
-
"fs-extra": "^11.2.0",
|
|
50
47
|
"hasha": "^6.0.0",
|
|
51
|
-
"ora": "^8.
|
|
48
|
+
"ora": "^8.2.0",
|
|
52
49
|
"yargs": "^17.7.2"
|
|
53
50
|
},
|
|
54
51
|
"devDependencies": {
|
|
55
|
-
"@kitschpatrol/shared-config": "^
|
|
56
|
-
"@types/node": "
|
|
57
|
-
"bumpp": "^
|
|
58
|
-
"chalk": "^5.
|
|
52
|
+
"@kitschpatrol/shared-config": "^5.0.4",
|
|
53
|
+
"@types/node": "^22.13.1",
|
|
54
|
+
"bumpp": "^10.0.2",
|
|
55
|
+
"chalk": "^5.4.1",
|
|
59
56
|
"lowdb": "^7.0.1",
|
|
60
|
-
"pkgroll": "^2.
|
|
61
|
-
"pretty-ms": "^9.
|
|
62
|
-
"typescript": "^5.
|
|
57
|
+
"pkgroll": "^2.8.0",
|
|
58
|
+
"pretty-ms": "^9.2.0",
|
|
59
|
+
"typescript": "^5.7.3",
|
|
63
60
|
"untildify": "^5.0.0"
|
|
64
61
|
},
|
|
62
|
+
"engines": {
|
|
63
|
+
"node": ">=22.0.0",
|
|
64
|
+
"pnpm": ">=10.1.0"
|
|
65
|
+
},
|
|
65
66
|
"publishConfig": {
|
|
66
67
|
"access": "public"
|
|
67
68
|
},
|
|
69
|
+
"cspell": {
|
|
70
|
+
"import": "@kitschpatrol/cspell-config"
|
|
71
|
+
},
|
|
72
|
+
"knip": {
|
|
73
|
+
"entry": [
|
|
74
|
+
"{index,cli,main}.{js,mjs,cjs,jsx,ts,tsx,mts,cts}!",
|
|
75
|
+
"src/{index,cli,main}.{js,mjs,cjs,jsx,ts,tsx,mts,cts}!",
|
|
76
|
+
"src/{bin,lib,cli}/{index,cli,main}.{js,mjs,cjs,jsx,ts,tsx,mts,cts}!",
|
|
77
|
+
"scripts/**/*.ts",
|
|
78
|
+
".remarkrc.js",
|
|
79
|
+
"cspell.config.js",
|
|
80
|
+
"eslint.config.ts",
|
|
81
|
+
"mdat.config.ts",
|
|
82
|
+
"prettier.config.js",
|
|
83
|
+
"stylelint.config.js"
|
|
84
|
+
],
|
|
85
|
+
"ignoreDependencies": [
|
|
86
|
+
"@kitschpatrol/cspell-config",
|
|
87
|
+
"@kitschpatrol/eslint-config",
|
|
88
|
+
"@kitschpatrol/knip-config",
|
|
89
|
+
"@kitschpatrol/mdat-config",
|
|
90
|
+
"@kitschpatrol/prettier-config",
|
|
91
|
+
"@kitschpatrol/remark-config",
|
|
92
|
+
"@kitschpatrol/stylelint-config"
|
|
93
|
+
]
|
|
94
|
+
},
|
|
95
|
+
"mdat": {
|
|
96
|
+
"$import": "node_modules/@kitschpatrol/mdat-config/dist/index.js"
|
|
97
|
+
},
|
|
98
|
+
"prettier": "@kitschpatrol/prettier-config",
|
|
99
|
+
"remarkConfig": {
|
|
100
|
+
"plugins": [
|
|
101
|
+
"@kitschpatrol/remark-config"
|
|
102
|
+
]
|
|
103
|
+
},
|
|
104
|
+
"stylelint": {
|
|
105
|
+
"extends": "@kitschpatrol/stylelint-config"
|
|
106
|
+
},
|
|
68
107
|
"scripts": {
|
|
69
|
-
"build": "pkgroll --clean-dist --minify",
|
|
108
|
+
"build": "pkgroll --clean-dist --minify --tsconfig tsconfig.build.json",
|
|
70
109
|
"clean": "git rm -f pnpm-lock.yaml ; git clean -fdX",
|
|
71
|
-
"fix": "
|
|
72
|
-
"lint": "
|
|
110
|
+
"fix": "kpi fix",
|
|
111
|
+
"lint": "kpi lint",
|
|
73
112
|
"release": "bumpp --commit 'Release: %s' && pnpm run build && pnpm publish --otp $(op read 'op://Personal/Npmjs/one-time password?attribute=otp')"
|
|
74
113
|
}
|
|
75
114
|
}
|
package/readme.md
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
<!-- /short-description -->
|
|
21
21
|
|
|
22
|
-
>
|
|
22
|
+
> [!WARNING]
|
|
23
23
|
> This tool was built in haste and has been tested only against a relatively narrow use-case. If you use it, please review the docs, invoke with care, and make sure you have local copies of your remote videos and metadata.
|
|
24
24
|
|
|
25
25
|
## Overview
|
|
@@ -123,7 +123,7 @@ vidup sync <directory> [options]
|
|
|
123
123
|
|
|
124
124
|
<!-- /cli-help -->
|
|
125
125
|
|
|
126
|
-
#### Examples
|
|
126
|
+
#### CLI Examples
|
|
127
127
|
|
|
128
128
|
**Synchronize video files in the current folder to Bunny Stream:**
|
|
129
129
|
|
|
@@ -160,13 +160,13 @@ The library exports `syncVideoInDirectory`, which takes an options argument mirr
|
|
|
160
160
|
async function syncVideoInDirectory(
|
|
161
161
|
directory: string,
|
|
162
162
|
options: {
|
|
163
|
-
service: Service
|
|
164
163
|
credentials: {
|
|
165
164
|
key: string
|
|
166
165
|
library: string
|
|
167
166
|
}
|
|
168
|
-
dryRun?: boolean //
|
|
169
|
-
|
|
167
|
+
dryRun?: boolean // Defaults to false
|
|
168
|
+
service: Service
|
|
169
|
+
verbose?: boolean // Defaults to false
|
|
170
170
|
},
|
|
171
171
|
): Promise<SyncReport>
|
|
172
172
|
```
|
|
@@ -177,23 +177,23 @@ A `stripVideoMetadataInDirectory` function is also exported, which will remove m
|
|
|
177
177
|
export async function stripVideoMetadataInFiles(
|
|
178
178
|
directory: string,
|
|
179
179
|
options: {
|
|
180
|
-
dryRun?: boolean //
|
|
181
|
-
verbose?: boolean //
|
|
180
|
+
dryRun?: boolean // Defaults to false
|
|
181
|
+
verbose?: boolean // Defaults to false
|
|
182
182
|
},
|
|
183
183
|
): Promise<string[]>
|
|
184
184
|
```
|
|
185
185
|
|
|
186
|
-
#### Examples
|
|
186
|
+
#### API Examples
|
|
187
187
|
|
|
188
188
|
```ts
|
|
189
189
|
import { syncVideoInDirectory } from 'vidup'
|
|
190
190
|
|
|
191
191
|
const syncReport = await syncVideoInDirectory(process.cwd(), {
|
|
192
|
-
service: 'bunny',
|
|
193
192
|
credentials: {
|
|
194
193
|
key: BUNNY_API_KEY,
|
|
195
194
|
library: BUNNY_LIBRARY_ID,
|
|
196
195
|
},
|
|
196
|
+
service: 'bunny',
|
|
197
197
|
})
|
|
198
198
|
|
|
199
199
|
console.log(JSON.stringify(syncReport, undefined, 2))
|
package/dist/sync-fEVhgE0O.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
var Z=Object.defineProperty;var i=(t,e)=>Z(t,"name",{value:e,configurable:!0});import{BunnyCdnStream as ee}from"bunnycdn-stream";import{hash as te}from"hasha";import{createReadStream as k}from"node:fs";import H,{writeFile as oe,rename as re,readFile as ne}from"node:fs/promises";import w,{join as ie,dirname as se,basename as ae}from"node:path";import{fileURLToPath as le}from"node:url";import{oraPromise as ce}from"ora";import{execa as G}from"execa";import j from"ffmpeg-static";const V=10,L=i((t=0)=>e=>`\x1B[${e+t}m`,"wrapAnsi16"),W=i((t=0)=>e=>`\x1B[${38+t};5;${e}m`,"wrapAnsi256"),Y=i((t=0)=>(e,o,r)=>`\x1B[${38+t};2;${e};${o};${r}m`,"wrapAnsi16m"),c={modifier:{reset:[0,0],bold:[1,22],dim:[2,22],italic:[3,23],underline:[4,24],overline:[53,55],inverse:[7,27],hidden:[8,28],strikethrough:[9,29]},color:{black:[30,39],red:[31,39],green:[32,39],yellow:[33,39],blue:[34,39],magenta:[35,39],cyan:[36,39],white:[37,39],blackBright:[90,39],gray:[90,39],grey:[90,39],redBright:[91,39],greenBright:[92,39],yellowBright:[93,39],blueBright:[94,39],magentaBright:[95,39],cyanBright:[96,39],whiteBright:[97,39]},bgColor:{bgBlack:[40,49],bgRed:[41,49],bgGreen:[42,49],bgYellow:[43,49],bgBlue:[44,49],bgMagenta:[45,49],bgCyan:[46,49],bgWhite:[47,49],bgBlackBright:[100,49],bgGray:[100,49],bgGrey:[100,49],bgRedBright:[101,49],bgGreenBright:[102,49],bgYellowBright:[103,49],bgBlueBright:[104,49],bgMagentaBright:[105,49],bgCyanBright:[106,49],bgWhiteBright:[107,49]}};Object.keys(c.modifier);const de=Object.keys(c.color),fe=Object.keys(c.bgColor);[...de,...fe];function ue(){const t=new Map;for(const[e,o]of Object.entries(c)){for(const[r,n]of Object.entries(o))c[r]={open:`\x1B[${n[0]}m`,close:`\x1B[${n[1]}m`},o[r]=c[r],t.set(n[0],n[1]);Object.defineProperty(c,e,{value:o,enumerable:!1})}return Object.defineProperty(c,"codes",{value:t,enumerable:!1}),c.color.close="\x1B[39m",c.bgColor.close="\x1B[49m",c.color.ansi=L(),c.color.ansi256=W(),c.color.ansi16m=Y(),c.bgColor.ansi=L(V),c.bgColor.ansi256=W(V),c.bgColor.ansi16m=Y(V),Object.defineProperties(c,{rgbToAnsi256:{value(e,o,r){return e===o&&o===r?e<8?16:e>248?231:Math.round((e-8)/247*24)+232:16+36*Math.round(e/255*5)+6*Math.round(o/255*5)+Math.round(r/255*5)},enumerable:!1},hexToRgb:{value(e){const o=/[a-f\d]{6}|[a-f\d]{3}/i.exec(e.toString(16));if(!o)return[0,0,0];let[r]=o;r.length===3&&(r=[...r].map(l=>l+l).join(""));const n=Number.parseInt(r,16);return[n>>16&255,n>>8&255,n&255]},enumerable:!1},hexToAnsi256:{value:i(e=>c.rgbToAnsi256(...c.hexToRgb(e)),"value"),enumerable:!1},ansi256ToAnsi:{value(e){if(e<8)return 30+e;if(e<16)return 90+(e-8);let o,r,n;if(e>=232)o=((e-232)*10+8)/255,r=o,n=o;else{e-=16;const m=e%36;o=Math.floor(e/36)/5,r=Math.floor(m/6)/5,n=m%6/5}const l=Math.max(o,r,n)*2;if(l===0)return 30;let u=30+(Math.round(n)<<2|Math.round(r)<<1|Math.round(o));return l===2&&(u+=60),u},enumerable:!1},rgbToAnsi:{value:i((e,o,r)=>c.ansi256ToAnsi(c.rgbToAnsi256(e,o,r)),"value"),enumerable:!1},hexToAnsi:{value:i(e=>c.ansi256ToAnsi(c.hexToAnsi256(e)),"value"),enumerable:!1}}),c}i(ue,"assembleStyles");const g=ue(),C=(()=>{if(navigator.userAgentData){const t=navigator.userAgentData.brands.find(({brand:e})=>e==="Chromium");if(t&&t.version>93)return 3}return/\b(Chrome|Chromium)\//.test(navigator.userAgent)?1:0})(),J=C!==0&&{level:C,hasBasic:!0,has256:C>=2,has16m:C>=3},he={stdout:J,stderr:J};function me(t,e,o){let r=t.indexOf(e);if(r===-1)return t;const n=e.length;let l=0,u="";do u+=t.slice(l,r)+e+o,l=r+n,r=t.indexOf(e,l);while(r!==-1);return u+=t.slice(l),u}i(me,"stringReplaceAll");function ge(t,e,o,r){let n=0,l="";do{const u=t[r-1]==="\r";l+=t.slice(n,u?r-1:r)+e+(u?`\r
|
|
2
|
-
`:`
|
|
3
|
-
`)+o,n=r+1,r=t.indexOf(`
|
|
4
|
-
`,n)}while(r!==-1);return l+=t.slice(n),l}i(ge,"stringEncaseCRLFWithFirstIndex");const{stdout:K,stderr:_}=he,N=Symbol("GENERATOR"),$=Symbol("STYLER"),B=Symbol("IS_EMPTY"),z=["ansi","ansi","ansi256","ansi16m"],F=Object.create(null),pe=i((t,e={})=>{if(e.level&&!(Number.isInteger(e.level)&&e.level>=0&&e.level<=3))throw new Error("The `level` option should be an integer from 0 to 3");const o=K?K.level:0;t.level=e.level===void 0?o:e.level},"applyOptions"),we=i(t=>{const e=i((...o)=>o.join(" "),"chalk");return pe(e,t),Object.setPrototypeOf(e,T.prototype),e},"chalkFactory");function T(t){return we(t)}i(T,"createChalk"),Object.setPrototypeOf(T.prototype,Function.prototype);for(const[t,e]of Object.entries(g))F[t]={get(){const o=I(this,U(e.open,e.close,this[$]),this[B]);return Object.defineProperty(this,t,{value:o}),o}};F.visible={get(){const t=I(this,this[$],!0);return Object.defineProperty(this,"visible",{value:t}),t}};const D=i((t,e,o,...r)=>t==="rgb"?e==="ansi16m"?g[o].ansi16m(...r):e==="ansi256"?g[o].ansi256(g.rgbToAnsi256(...r)):g[o].ansi(g.rgbToAnsi(...r)):t==="hex"?D("rgb",e,o,...g.hexToRgb(...r)):g[o][t](...r),"getModelAnsi"),ye=["rgb","hex","ansi256"];for(const t of ye){F[t]={get(){const{level:o}=this;return function(...r){const n=U(D(t,z[o],"color",...r),g.color.close,this[$]);return I(this,n,this[B])}}};const e="bg"+t[0].toUpperCase()+t.slice(1);F[e]={get(){const{level:o}=this;return function(...r){const n=U(D(t,z[o],"bgColor",...r),g.bgColor.close,this[$]);return I(this,n,this[B])}}}}const be=Object.defineProperties(()=>{},{...F,level:{enumerable:!0,get(){return this[N].level},set(t){this[N].level=t}}}),U=i((t,e,o)=>{let r,n;return o===void 0?(r=t,n=e):(r=o.openAll+t,n=e+o.closeAll),{open:t,close:e,openAll:r,closeAll:n,parent:o}},"createStyler"),I=i((t,e,o)=>{const r=i((...n)=>ve(r,n.length===1?""+n[0]:n.join(" ")),"builder");return Object.setPrototypeOf(r,be),r[N]=t,r[$]=e,r[B]=o,r},"createBuilder"),ve=i((t,e)=>{if(t.level<=0||!e)return t[B]?"":e;let o=t[$];if(o===void 0)return e;const{openAll:r,closeAll:n}=o;if(e.includes("\x1B"))for(;o!==void 0;)e=me(e,o.close,o.open),o=o.parent;const l=e.indexOf(`
|
|
5
|
-
`);return l!==-1&&(e=ge(e,n,r,l)),r+e+n},"applyStyle");Object.defineProperties(T.prototype,F);const p=T();T({level:_?_.level:0});const q=process?.versions?.node!==void 0,d={verbose:!1,log(...t){if(!this.verbose)return;const e=p.gray("[Log]");q?console.warn(e,...t):console.log(e,...t)},logPrefixed(t,...e){this.info(p.blue(`[${t}]`),...e)},info(...t){if(!this.verbose)return;const e=p.green("[Info]");q?console.warn(e,...t):console.info(e,...t)},infoPrefixed(t,...e){this.info(p.blue(`[${t}]`),...e)},async infoSpin(t,e){return ce(t,{prefixText:p.green("[Info]"),stream:process.stderr,text:e})},warn(...t){console.warn(p.yellow("[Warning]"),...t)},warnPrefixed(t,...e){this.warn(p.blue(`[${t}]`),...e)},error(...t){console.error(p.red("[Error]"),...t)},errorPrefixed(t,...e){this.error(p.blue(`[${t}]`),...e)}};async function Q(t){return(await H.readdir(t)).filter(r=>{const n=w.extname(r);return[".mp4",".mov",".avi",".mkv"].includes(n)}).map(r=>w.join(t,r))}i(Q,"getVideosInDirectory");async function $e(t){if(j===null)throw new Error("ffmpeg-static failed to locate ffmpeg binary");try{const{stderr:e,stdout:o}=await G(j,["-i",t,"-f","ffmetadata","-"]);return o.includes("creation_time")||e.includes("creation_time")}catch(e){throw new Error(`Failed to check metadata for: ${t}:
|
|
6
|
-
${String(e)}`)}}i($e,"videoHasMetadata");async function Fe(t){if(j===null)throw new Error("ffmpeg-static failed to locate ffmpeg binary");try{const e=w.extname(t).slice(1),o=`${t.slice(0,Math.max(0,t.lastIndexOf(".")))}.temp.${e}`;await G(j,["-i",t,"-map_metadata","-1","-c:v","copy","-c:a","copy",o]),await H.rename(o,t)}catch(e){throw new Error(`Failed to strip metadata from: ${t}:
|
|
7
|
-
${String(e)}`)}}i(Fe,"stripVideoMetadata");async function xe(t,e={}){const{dryRun:o=!1,verbose:r=!1}=e,n=await Q(t);return Se(n,{dryRun:o,verbose:r})}i(xe,"stripVideoMetadataInDirectory");async function Se(t,e={}){const{dryRun:o=!1,verbose:r=!1}=e,n=d.verbose;d.verbose=r;const l=[];for(const u of t)await $e(u)&&l.push(u);if(!o){d.info(`Found ${l.length} videos with metadata to strip`);for(const u of l)console.log(`Stripping metadata from: ${u}`),await Fe(u)}return d.verbose=n,l}i(Se,"stripVideoMetadataInFiles");function Ee(t){const e=t instanceof URL?le(t):t.toString();return ie(se(e),`.${ae(e)}.tmp`)}i(Ee,"getTempFilename");async function Oe(t,e,o){for(let r=0;r<e;r++)try{return await t()}catch(n){if(r<e-1)await new Promise(l=>setTimeout(l,o));else throw n}}i(Oe,"retryAsyncOperation");class Ae{static{i(this,"Writer")}#e;#t;#o=!1;#n=null;#i=null;#s=null;#r=null;#a(e){return this.#r=e,this.#s||=new Promise((o,r)=>{this.#i=[o,r]}),new Promise((o,r)=>{this.#s?.then(o).catch(r)})}async#l(e){this.#o=!0;try{await oe(this.#t,e,"utf-8"),await Oe(async()=>{await re(this.#t,this.#e)},10,100),this.#n?.[0]()}catch(o){throw o instanceof Error&&this.#n?.[1](o),o}finally{if(this.#o=!1,this.#n=this.#i,this.#i=this.#s=null,this.#r!==null){const o=this.#r;this.#r=null,await this.write(o)}}}constructor(e){this.#e=e,this.#t=Ee(e)}async write(e){return this.#o?this.#a(e):this.#l(e)}}class Be{static{i(this,"TextFile")}#e;#t;constructor(e){this.#e=e,this.#t=new Ae(e)}async read(){let e;try{e=await ne(this.#e,"utf-8")}catch(o){if(o.code==="ENOENT")return null;throw o}return e}write(e){return this.#t.write(e)}}class Te{static{i(this,"DataFile")}#e;#t;#o;constructor(e,{parse:o,stringify:r}){this.#e=new Be(e),this.#t=o,this.#o=r}async read(){const e=await this.#e.read();return e===null?null:this.#t(e)}write(e){return this.#e.write(this.#o(e))}}class Pe extends Te{static{i(this,"JSONFile")}constructor(e){super(e,{parse:JSON.parse,stringify:i(o=>JSON.stringify(o,null,2),"stringify")})}}class Re{static{i(this,"Memory")}#e=null;read(){return Promise.resolve(this.#e)}write(e){return this.#e=e,Promise.resolve()}}function je(t,e){if(t===void 0)throw new Error("lowdb: missing adapter");if(e===void 0)throw new Error("lowdb: missing default data")}i(je,"checkArgs");class Ce{static{i(this,"Low")}adapter;data;constructor(e,o){je(e,o),this.adapter=e,this.data=o}async read(){const e=await this.adapter.read();e&&(this.data=e)}async write(){this.data&&await this.adapter.write(this.data)}async update(e){e(this.data),await this.write()}}async function Ie(t,e){const o=process.env.NODE_ENV==="test"?new Re:new Pe(t),r=new Ce(o,e);return await r.read(),r}i(Ie,"JSONFilePreset");async function Me(t,e){const{credentials:o,dryRun:r=!1,service:n,verbose:l=!1}=e,u=d.verbose;d.verbose=l;const m=[],P=await Q(t);d.info(`Found ${P.length} video files in directory "${t}"`);const f=await Ie(w.join(t,".vidup-state.json"),{syncState:[]});f.data.syncState=f.data.syncState.filter(s=>P.includes(w.join(t,s.filename)));for(const s of P){const a=w.basename(s),h=await te(s,{algorithm:"sha1"}),y=f.data.syncState.find(b=>b.filename===a);y?y.localHash=h:f.data.syncState.push({filename:a,localHash:h,remoteHash:{}})}if(r||await f.write(),n!=="bunny")throw new Error(`Streaming service not yet implemented: ${n}`);const x=new ee({apiKey:o.key,videoLibrary:o.library}),S=await x.listAllVideos();d.info(`Found ${S.length} video files on ${n} remote streaming service`);const X=S.filter(s=>{const a=f.data.syncState.find(h=>h.filename===s.title);return a!==void 0&&a.localHash===a.remoteHash[n]});for(const s of X)m.push({action:"None",localFile:s.title,remoteId:s.guid});const E=f.data.syncState.filter(s=>!S.some(a=>a.title===s.filename));for(const s of E)m.push({action:"Create",localFile:s.filename,remoteId:"Not yet uploaded (Dry run)"});const O=S.filter(s=>{const a=f.data.syncState.find(h=>h.filename===s.title);return a!==void 0&&a.localHash!==a.remoteHash[n]});for(const s of O)m.push({action:"Update",localFile:s.title,remoteId:"Not yet uploaded (Dry run)"});const R=S.filter(s=>!f.data.syncState.some(a=>a.filename===s.title));for(const s of R)m.push({action:"Delete",localFile:s.title,remoteId:s.guid});if(!r){f.data.lastRun=new Date,await f.write(),d.info("Synchronizing...");for(const[s,a]of R.entries()){if(s===0&&d.info(`Deleting ${R.length} remote videos...`),!(await d.infoSpin(x.deleteVideo(a.guid),`Deleting remote video ${s+1}/${R.length}: ${a.title}`)).success)throw new Error(`Failed to delete remote video: ${a.title}`);f.data.lastUpdate=new Date,await f.write()}for(const[s,a]of E.entries()){s===0&&d.info(`Uploading ${E.length} new local videos...`);const h=k(w.join(t,a.filename)),y=await d.infoSpin(x.createAndUploadVideo(h,{title:a.filename}),`Uploading new video ${s+1}/${E.length}: ${a.filename}`);d.info(`Remote video ${s+1}/${E.length} created with GUID: ${y.guid}`);const b=f.data.syncState.find(v=>v.filename===a.filename);if(b===void 0)throw new Error(`Failed to find state entry for: ${a.filename}`);b.remoteHash[n]=a.localHash,f.data.lastUpdate=new Date,await f.write();const A=m.find(v=>v.localFile===a.filename);if(A===void 0)throw new Error(`Failed to find report entry for: ${a.filename}`);A.remoteId=y.guid}for(const[s,a]of O.entries()){s===0&&(d.info(`Updating ${O.length} remote videos...`),d.warn("Bunny does not currently support updating videos in-place, so vidup will delete and re-uploading instead... this will change the video's GUID!"));const h=f.data.syncState.find(M=>M.filename===a.title);if(h===void 0)throw new Error(`Failed to find state entry for: ${a.title}`);if(!(await x.deleteVideo(a.guid)).success)throw new Error(`Failed to delete remote video before updating: ${a.title}`);const b=k(w.join(t,h.filename)),A=await d.infoSpin(x.createAndUploadVideo(b,{title:h.filename}),`Updating remote video ${s+1}/${O.length}: ${a.title}`);d.info(`Updated remote video ${s+1}/${O.length} created with GUID: ${A.guid}`),h.remoteHash[n]=h.localHash,f.data.lastUpdate=new Date,await f.write();const v=m.find(M=>M.localFile===h.filename);if(v===void 0)throw new Error(`Failed to find report entry for: ${h.filename}`);v.remoteId=A.guid}d.info(`All ${P.length} videos are now in sync!`)}return d.verbose=u,m}i(Me,"syncVideoInDirectory");export{Me as a,d as l,xe as s};
|