@@ -59,9 +59,169 @@ export default {
5959 // reference to Cached
6060 window . ufs_tiktok_batchDownload = CACHED ;
6161
62+ const id = "ufs_tiktok_batchDownload" ;
63+ const floatingBtnId = `${ id } _floating_btn` ;
64+ const containerId = `${ id } _container` ;
65+
66+ const ui = document . createElement ( "div" ) ;
67+ ui . id = id ;
68+ ( document . body || document . documentElement ) . appendChild ( ui ) ;
69+
70+ ui . innerHTML = /*html*/ `
71+ <style>
72+ #${ id } {
73+ position: fixed;
74+ z-index: 99999999999;
75+ }
76+ #${ floatingBtnId } {
77+ border-radius: 25px;
78+ background: #444;
79+ color: white;
80+ padding: 15px;
81+ position: fixed;
82+ bottom: 25px;
83+ right: 25px;
84+ border: 1px solid #777;
85+ cursor: pointer;
86+ }
87+ #${ floatingBtnId } :hover {
88+ background: #666;
89+ }
90+ #${ containerId } {
91+ display: none;
92+ position: fixed;
93+ top: 0;
94+ left: 0;
95+ width: 100vw;
96+ height: 100vh;
97+ background: rgba(0, 0, 0, 0.5);
98+ align-items: center;
99+ justify-content: center;
100+ }
101+ .ufs_popup {
102+ background: #444;
103+ color: #eee;
104+ padding: 20px;
105+ border-radius: 10px;
106+ max-width: 90vw;
107+ max-height: 90vh;
108+ overflow-y: auto;
109+ overflow-x: hidden;
110+ }
111+ .ufs_popup table, .ufs_popup th, .ufs_popup td {
112+ border: 1px solid #aaa;
113+ border-collapse: collapse;
114+ }
115+ .ufs_popup table td {
116+ padding: 10px;
117+ }
118+ .ufs_popup table th {
119+ position: sticky;
120+ top: -22px;
121+ background: #333;
122+ }
123+ </style>
124+
125+ <div id="${ floatingBtnId } ">📥</div>
126+
127+ <div id="${ containerId } ">
128+
129+ </div>
130+ ` ;
131+ const floatingBtn = ui . querySelector ( "#" + floatingBtnId ) ;
132+ const container = ui . querySelector ( "#" + containerId ) ;
133+
134+ function toggle ( willShow ) {
135+ if ( ! ( typeof willShow === "boolean" ) ) {
136+ let isShowing = container . style . display == "flex" ;
137+ willShow = ! isShowing ;
138+ }
139+ container . style . display = willShow ? "flex" : "none" ;
140+ return willShow ;
141+ }
142+
143+ function renderData ( ) {
144+ container . innerHTML = /*html*/ `
145+ <div class="ufs_popup">
146+ <h1 style="text-align:center">Tiktok - Useful Scripts</h1>
147+ <h2 style="text-align:center">Found ${ CACHED . videoById . size } videos</h2>
148+
149+ <table>
150+ <thead>
151+ <tr>
152+ <th data-sort="index">#</th>
153+ <th>Video</th>
154+ <th data-sort="title">Title</th>
155+ <th data-sort="user">User</th>
156+ <th data-sort="view">View</th>
157+ <th data-sort="length">Length</th>
158+ <th>Download</th>
159+ </tr>
160+ </thead>
161+ <tbody>
162+ </tbody>
163+ </table>
164+ </div>` ;
165+
166+ const tbody = container . querySelector ( "tbody" ) ;
167+ renderTable ( tbody ) ;
168+ }
169+
170+ function renderTable (
171+ tbody ,
172+ data = Array . from ( CACHED . videoById . values ( ) )
173+ ) {
174+ tbody . innerHTML = data
175+ . map (
176+ ( v , i ) => /*html*/ `
177+ <tr>
178+ <td>${ i + 1 } </td>
179+ <td>
180+ <a target="_blank" href="${ v . video . playAddr } ">
181+ <img src="${ v . video . originCover } " style="width:150px" />
182+ </a>
183+ </td>
184+ <td><p style="max-width:200px">${ v . desc } </p></td>
185+ <td>
186+ <a target="_blank" href="https://www.tiktok.com/@${ v . author . uniqueId } ">
187+ <img src="${ v . author . avatarThumb } " style="width:50px;height:50px" />
188+ </a>
189+ ${ v . author . uniqueId } <br/>
190+ ${ v . author . nickname } <br/>
191+ ${ v . id }
192+ </td>
193+ <td>${ UfsGlobal . Utils . moneyFormat ( v . stats . playCount ) } </td>
194+ <td>${ v . video . duration } s</td>
195+ <td>
196+ <p style="max-width:200px">
197+ <a href="${ v . video . playAddr } " download target="_blank">Video</a><br/>
198+ <a href="${ v . author . avatarLarger } " download target="_blank">
199+ Avatar
200+ </a><br/>
201+ <a href="${ v . music . playUrl } " download target="_blank">
202+ Music: ${ v . music . title }
203+ </a>
204+ </p>
205+ </td>
206+ </tr>`
207+ )
208+ . join ( "" ) ;
209+ }
210+
211+ container . onclick = ( e ) => {
212+ if ( e . target === container ) {
213+ toggle ( false ) ;
214+ }
215+ } ;
216+
217+ floatingBtn . onclick = ( ) => {
218+ let isShow = toggle ( ) ;
219+ if ( isShow ) renderData ( ) ;
220+ } ;
221+
62222 hookFetch ( {
63223 onAfter : async ( url , options , response ) => {
64- if ( url . includes ( "api/post/ item_list" ) ) {
224+ if ( url . includes ( "item_list/ " ) ) {
65225 // clone to new response
66226 const res = response . clone ( ) ;
67227 const json = await res . json ( ) ;
@@ -70,17 +230,15 @@ export default {
70230 if ( json ?. itemList ) {
71231 CACHED . list . push ( ...json . itemList ) ;
72232 json . itemList . forEach ( ( _ ) => {
73- CACHED . videoById . set ( _ . video . id , {
74- url : _ . video . playAddr ,
75- name : _ . desc ,
76- } ) ;
233+ if ( _ . video . playAddr ) CACHED . videoById . set ( _ . video . id , _ ) ;
77234 } ) ;
235+ floatingBtn . innerHTML = `📥 (${ CACHED . videoById . size } )` ;
78236 }
79237 }
80238 } ,
81239 } ) ;
82240 } ,
83- onDocumentIdle : async ( ) => {
241+ _onDocumentIdle : async ( ) => {
84242 let checkboxes = [ ] ;
85243
86244 // Setup DOM
0 commit comments