jebin2 commited on
Commit
95b1a71
·
1 Parent(s): 3a1dbcc

Cleanup: Remove video_analyser and unused downloader

Browse files
src/video_downloader.py DELETED
@@ -1,724 +0,0 @@
1
- """
2
- Video Downloader Utility
3
- Download videos by filename from Google Drive
4
- """
5
-
6
- import os
7
- import csv
8
- from pathlib import Path
9
- import sys
10
- import re
11
- from typing import Optional, List, Dict
12
- import shutil
13
- from concurrent.futures import ThreadPoolExecutor, as_completed
14
- import threading
15
-
16
- # Try to import logger from utils, fallback to print
17
- try:
18
- from utils import logger
19
- except ImportError:
20
- class SimpleLogger:
21
- @staticmethod
22
- def info(msg): print(f"INFO: {msg}")
23
- @staticmethod
24
- def error(msg): print(f"ERROR: {msg}")
25
- @staticmethod
26
- def warning(msg): print(f"WARNING: {msg}")
27
- logger = SimpleLogger()
28
-
29
-
30
- class VideoDownloader:
31
- def __init__(self, csv_path: Optional[str] = None, config_path: Optional[str] = None):
32
- """
33
- Initialize VideoDownloader
34
-
35
- Args:
36
- csv_path: Path to CSV file containing video library
37
- Defaults to infloxa_video_analysis.csv
38
- config_path: Path to config.yaml for DriveDownloader
39
- Defaults to video_analyser/config.yaml
40
- """
41
- if csv_path is None:
42
- csv_path = "video_analyser/infloxa_video_analysis.csv"
43
-
44
- if config_path is None:
45
- config_path = "video_analyser/config.yaml"
46
-
47
- self.csv_path = csv_path
48
- self.config_path = config_path
49
- self.video_library = self._load_video_library()
50
- self.drive_downloader = None
51
- self.lock = threading.Lock() # Thread-safe lock for drive operations
52
-
53
- def _load_video_library(self) -> List[Dict]:
54
- """Load video library from CSV file"""
55
- try:
56
- if not os.path.exists(self.csv_path):
57
- logger.error(f"CSV file not found: {self.csv_path}")
58
- return []
59
-
60
- videos = []
61
- with open(self.csv_path, 'r', encoding='utf-8') as f:
62
- reader = csv.DictReader(f)
63
- for row in reader:
64
- videos.append(row)
65
-
66
- logger.info(f"Loaded video library with {len(videos)} entries from {self.csv_path}")
67
- return videos
68
-
69
- except Exception as e:
70
- logger.error(f"Failed to load video library: {e}")
71
- raise
72
-
73
- def _init_drive_downloader(self, download_path: str):
74
- """Initialize Google Drive downloader"""
75
- if self.drive_downloader is not None:
76
- return
77
-
78
- try:
79
- # Add video_analyser to path
80
- video_analyser_path = os.path.join(os.path.dirname(__file__), '..', 'video_analyser')
81
- if video_analyser_path not in sys.path:
82
- sys.path.insert(0, video_analyser_path)
83
-
84
- from modules import DriveDownloader
85
- import yaml
86
-
87
- if not os.path.exists(self.config_path):
88
- logger.error(f"Config file not found: {self.config_path}")
89
- raise FileNotFoundError(f"Config file not found: {self.config_path}")
90
-
91
- with open(self.config_path, 'r') as f:
92
- self.config = yaml.safe_load(f)
93
-
94
- # Override the local directory with our download path
95
- self.config['output']['local_video_dir'] = download_path
96
-
97
- # Initialize and authenticate
98
- logger.info("Initializing Google Drive connection...")
99
- self.drive_downloader = DriveDownloader(self.config)
100
- self.drive_downloader.authenticate()
101
- logger.info("✓ Google Drive authenticated")
102
-
103
- except Exception as e:
104
- logger.error(f"Failed to initialize DriveDownloader: {e}")
105
- raise
106
-
107
- def _get_thread_service(self):
108
- """Get a thread-local Google Drive service instance"""
109
- import threading
110
- thread_id = threading.get_ident()
111
-
112
- # Check if this thread already has a service
113
- if not hasattr(self, '_thread_services'):
114
- self._thread_services = {}
115
-
116
- if thread_id not in self._thread_services:
117
- # Create a new service for this thread
118
- from modules import DriveDownloader
119
- import yaml
120
-
121
- with open(self.config_path, 'r') as f:
122
- config = yaml.safe_load(f)
123
-
124
- downloader = DriveDownloader(config)
125
- downloader.authenticate()
126
- self._thread_services[thread_id] = downloader.service
127
- logger.info(f"Created new Drive service for thread {thread_id}")
128
-
129
- return self._thread_services[thread_id]
130
-
131
- def _extract_folder_id_from_link(self, drive_link: str) -> Optional[str]:
132
- """
133
- Extract folder ID from Google Drive link
134
-
135
- Args:
136
- drive_link: Google Drive folder URL
137
-
138
- Returns:
139
- Folder ID or None if not found
140
- """
141
- patterns = [
142
- r'folders/([a-zA-Z0-9_-]+)',
143
- r'id=([a-zA-Z0-9_-]+)',
144
- ]
145
-
146
- for pattern in patterns:
147
- match = re.search(pattern, drive_link)
148
- if match:
149
- return match.group(1)
150
-
151
- logger.error(f"Could not extract folder ID from link: {drive_link}")
152
- return None
153
-
154
- def _list_folder_contents_recursive(self, folder_id: str, parent_path: str = "") -> List[Dict]:
155
- """
156
- Recursively list all files in a folder and its subfolders
157
-
158
- Args:
159
- folder_id: Google Drive folder ID
160
- parent_path: Path of parent folder for tracking structure
161
-
162
- Returns:
163
- List of dictionaries with file info including relative path
164
- """
165
- try:
166
- files_and_folders = []
167
-
168
- # Query for all items in this folder
169
- query = f"'{folder_id}' in parents and trashed=false"
170
- results = self.drive_downloader.service.files().list(
171
- q=query,
172
- fields="files(id, name, mimeType, webViewLink)",
173
- pageSize=1000
174
- ).execute()
175
-
176
- items = results.get('files', [])
177
-
178
- for item in items:
179
- item_name = item['name']
180
- item_id = item['id']
181
- mime_type = item['mimeType']
182
-
183
- # Build current path
184
- current_path = os.path.join(parent_path, item_name) if parent_path else item_name
185
-
186
- if mime_type == 'application/vnd.google-apps.folder':
187
- # It's a folder - recurse into it
188
- logger.info(f"Scanning folder: {current_path}")
189
- subfolder_contents = self._list_folder_contents_recursive(item_id, current_path)
190
- files_and_folders.extend(subfolder_contents)
191
- else:
192
- # It's a file
193
- files_and_folders.append({
194
- 'id': item_id,
195
- 'name': item_name,
196
- 'path': current_path,
197
- 'mimeType': mime_type,
198
- 'webViewLink': item.get('webViewLink', '')
199
- })
200
-
201
- return files_and_folders
202
-
203
- except Exception as e:
204
- logger.error(f"Error listing folder contents: {e}")
205
- return []
206
-
207
- def _download_single_file(
208
- self,
209
- file_info: Dict,
210
- download_root: str,
211
- idx: int,
212
- total: int
213
- ) -> Dict[str, any]:
214
- """
215
- Download a single file from Google Drive (for parallel execution)
216
-
217
- Args:
218
- file_info: Dictionary with file information
219
- download_root: Root directory for downloads
220
- idx: Current file index
221
- total: Total number of files
222
-
223
- Returns:
224
- Dictionary with download result
225
- """
226
- result = {
227
- 'file': file_info['name'],
228
- 'status': 'unknown',
229
- 'path': None,
230
- 'error': None
231
- }
232
-
233
- try:
234
- # Build local path preserving folder structure
235
- relative_path = file_info['path']
236
- local_path = os.path.join(download_root, relative_path)
237
- local_dir = os.path.dirname(local_path)
238
-
239
- # Check if file already exists
240
- if os.path.exists(local_path):
241
- logger.info(f"[{idx}/{total}] Skipped (exists): {relative_path}")
242
- result['status'] = 'skipped'
243
- result['path'] = local_path
244
- return result
245
-
246
- # Create directory structure BEFORE downloading
247
- os.makedirs(local_dir, exist_ok=True)
248
- logger.info(f"[{idx}/{total}] Downloading: {relative_path}")
249
-
250
- # Get thread-local service instance
251
- service = self._get_thread_service()
252
-
253
- # Download file DIRECTLY to the final destination
254
- request = service.files().get_media(fileId=file_info['id'])
255
-
256
- with open(local_path, 'wb') as f:
257
- from googleapiclient.http import MediaIoBaseDownload
258
- downloader = MediaIoBaseDownload(f, request)
259
- done = False
260
- last_progress = 0
261
- while not done:
262
- status, done = downloader.next_chunk()
263
- if status:
264
- progress = int(status.progress() * 100)
265
- # Log every 25% to avoid spam
266
- if progress >= last_progress + 25:
267
- logger.info(f" [{file_info['name']}] Progress: {progress}%")
268
- last_progress = progress
269
-
270
- logger.info(f"✓ Successfully downloaded: {local_path}")
271
- result['status'] = 'downloaded'
272
- result['path'] = local_path
273
-
274
- except Exception as e:
275
- logger.error(f"Failed to download {file_info['name']}: {e}")
276
- result['status'] = 'failed'
277
- result['error'] = str(e)
278
-
279
- # Clean up partial download if it exists
280
- if 'local_path' in locals() and os.path.exists(local_path):
281
- try:
282
- os.remove(local_path)
283
- except:
284
- pass
285
-
286
- return result
287
-
288
- def download_from_drive_link(
289
- self,
290
- drive_link: str,
291
- download_root: str,
292
- file_extensions: Optional[List[str]] = None,
293
- max_workers: int = 10 # Number of parallel downloads
294
- ) -> Dict[str, any]:
295
- """
296
- Download all files from a Google Drive folder link, preserving folder structure
297
- (with parallel downloads)
298
-
299
- Args:
300
- drive_link: Google Drive folder URL
301
- e.g., https://drive.google.com/drive/folders/1WSrVAyqvPJzpRnoUxkNx0LqK9VlDs432
302
- download_root: Root directory where files should be downloaded
303
- file_extensions: Optional list of file extensions to filter (e.g., ['.mp4', '.avi'])
304
- If None, downloads all files
305
- max_workers: Number of parallel downloads (default: 10)
306
-
307
- Returns:
308
- Dictionary with download statistics:
309
- {
310
- 'total_files': int,
311
- 'downloaded': int,
312
- 'skipped': int,
313
- 'failed': int,
314
- 'files': List[str] # paths of downloaded files
315
- }
316
-
317
- Example:
318
- >>> downloader = VideoDownloader()
319
- >>> result = downloader.download_from_drive_link(
320
- ... drive_link="https://drive.google.com/drive/folders/1WSrVAyqvPJzpRnoUxkNx0LqK9VlDs432",
321
- ... download_root="downloads/my_videos",
322
- ... file_extensions=['.mp4', '.mov', '.avi'],
323
- ... max_workers=10
324
- ... )
325
- >>> print(f"Downloaded {result['downloaded']} files")
326
- """
327
- try:
328
- # Initialize Drive downloader (pass None to avoid auto path setup)
329
- self._init_drive_downloader(None)
330
-
331
- # Extract folder ID from link
332
- folder_id = self._extract_folder_id_from_link(drive_link)
333
- if not folder_id:
334
- return {
335
- 'total_files': 0,
336
- 'downloaded': 0,
337
- 'skipped': 0,
338
- 'failed': 0,
339
- 'files': []
340
- }
341
-
342
- logger.info(f"Scanning Google Drive folder: {folder_id}")
343
-
344
- # Get all files recursively
345
- all_files = self._list_folder_contents_recursive(folder_id)
346
-
347
- # Filter by file extensions if provided
348
- if file_extensions:
349
- file_extensions = [ext.lower() if ext.startswith('.') else f'.{ext.lower()}'
350
- for ext in file_extensions]
351
- all_files = [f for f in all_files
352
- if any(f['name'].lower().endswith(ext) for ext in file_extensions)]
353
-
354
- logger.info(f"Found {len(all_files)} files to download")
355
-
356
- # Statistics
357
- stats = {
358
- 'total_files': len(all_files),
359
- 'downloaded': 0,
360
- 'skipped': 0,
361
- 'failed': 0,
362
- 'files': []
363
- }
364
-
365
- # Download files in parallel using ThreadPoolExecutor
366
- logger.info(f"Starting parallel downloads with {max_workers} workers...")
367
-
368
- with ThreadPoolExecutor(max_workers=max_workers) as executor:
369
- # Submit all download tasks
370
- future_to_file = {
371
- executor.submit(
372
- self._download_single_file,
373
- file_info,
374
- download_root,
375
- idx,
376
- len(all_files)
377
- ): file_info
378
- for idx, file_info in enumerate(all_files, 1)
379
- }
380
-
381
- # Collect results as they complete
382
- for future in as_completed(future_to_file):
383
- file_info = future_to_file[future]
384
- try:
385
- result = future.result()
386
-
387
- if result['status'] == 'downloaded':
388
- stats['downloaded'] += 1
389
- if result['path']:
390
- stats['files'].append(result['path'])
391
- elif result['status'] == 'skipped':
392
- stats['skipped'] += 1
393
- if result['path']:
394
- stats['files'].append(result['path'])
395
- elif result['status'] == 'failed':
396
- stats['failed'] += 1
397
-
398
- except Exception as e:
399
- logger.error(f"Error processing {file_info['name']}: {e}")
400
- stats['failed'] += 1
401
-
402
- # Summary
403
- logger.info("=" * 60)
404
- logger.info("Download Summary:")
405
- logger.info(f" Total files: {stats['total_files']}")
406
- logger.info(f" Downloaded: {stats['downloaded']}")
407
- logger.info(f" Skipped (already exist): {stats['skipped']}")
408
- logger.info(f" Failed: {stats['failed']}")
409
- logger.info("=" * 60)
410
-
411
- return stats
412
-
413
- except Exception as e:
414
- logger.error(f"Error downloading from drive link: {e}")
415
- import traceback
416
- traceback.print_exc()
417
- return {
418
- 'total_files': 0,
419
- 'downloaded': 0,
420
- 'skipped': 0,
421
- 'failed': 0,
422
- 'files': []
423
- }
424
-
425
- def get_folder_name(
426
- self,
427
- video_filename: str,
428
- ) -> str:
429
- try:
430
- # Initialize Drive downloader
431
- self._init_drive_downloader(None)
432
-
433
- # List all videos from Google Drive
434
- logger.info(f"Searching for '{video_filename}' in Google Drive...")
435
- all_videos = self.drive_downloader.list_all_videos()
436
-
437
- # Find the video by filename
438
- matching_video = None
439
- for video_item in all_videos:
440
- if video_item['name'] == video_filename:
441
- matching_video = video_item
442
- break
443
-
444
- if not matching_video:
445
- return None
446
- else:
447
- return matching_video["path"].split("/")[0]
448
- except: return None
449
-
450
-
451
-
452
- def get_video_link(self, video_filename: str) -> Optional[str]:
453
- """Fetches the Google Drive webViewLink for the file"""
454
- try:
455
- self._init_drive_downloader(None)
456
- all_videos = self.drive_downloader.list_all_videos()
457
- for video_item in all_videos:
458
- if video_item['name'] == video_filename:
459
- # Return the webLink if available, otherwise construct one from ID
460
- return video_item.get('webViewLink') or f"https://drive.google.com/file/d/{video_item.get('id')}/view"
461
- return None
462
- except Exception as e:
463
- logger.error(f"Error fetching link for {video_filename}: {e}")
464
- return None
465
-
466
- def download_video(
467
- self,
468
- video_filename: str,
469
- download_path: str
470
- ) -> bool:
471
- """
472
- Download a video from Google Drive by filename
473
-
474
- Args:
475
- video_filename: Name of the video file to search for in Google Drive
476
- download_path: Directory path where video should be downloaded
477
-
478
- Returns:
479
- True if download successful, False otherwise
480
-
481
- Example:
482
- >>> downloader = VideoDownloader()
483
- >>> downloader.download_video(
484
- ... video_filename="Copy of SnapInsta.to_AQM-w-YmCepAaXIWl7Frs10cKbdX1P-__zSTTzh7j0td9hh6ebAJWA409L_fJNcgiWctukdKl0ubkT2tNOxgjyRdOXs3nPBgMAgWV90.mp4",
485
- ... download_path="downloads/videos"
486
- ... )
487
- """
488
- try:
489
- save_path = os.path.join(download_path, video_filename)
490
- if os.path.exists(save_path):
491
- logger.info(f"File already exists: {save_path}")
492
- return save_path
493
- # Initialize Drive downloader
494
- self._init_drive_downloader(download_path)
495
-
496
- # List all videos from Google Drive
497
- logger.info(f"Searching for '{video_filename}' in Google Drive...")
498
- all_videos = self.drive_downloader.list_all_videos()
499
-
500
- # Find the video by filename
501
- matching_video = None
502
- for video_item in all_videos:
503
- if video_item['name'] == video_filename:
504
- matching_video = video_item
505
- break
506
-
507
- if not matching_video:
508
- logger.error(f"Video not found in Google Drive: {video_filename}")
509
- logger.info(f"Searched {len(all_videos)} videos in Drive")
510
-
511
- # Show similar filenames for debugging
512
- similar = [v['name'] for v in all_videos if video_filename[:30] in v['name']]
513
- if similar:
514
- logger.info(f"Similar files found: {similar[:3]}")
515
- return None
516
- else:
517
- logger.info(f"Found video: {matching_video}")
518
- # Create download directory
519
- os.makedirs(download_path, exist_ok=True)
520
-
521
- # Download the video
522
- logger.info(f"Downloading {video_filename}...")
523
- local_path = self.drive_downloader.download_single_video(matching_video)
524
- shutil.move(local_path, save_path)
525
-
526
- if save_path:
527
- logger.info(f"✓ Successfully downloaded to: {save_path}")
528
- return save_path
529
- else:
530
- logger.error(f"Failed to download {video_filename}")
531
- return None
532
-
533
- except Exception as e:
534
- logger.error(f"Error downloading video: {e}")
535
- import traceback
536
- traceback.print_exc()
537
- return None
538
-
539
- def get_videos_by_category(self, category: str) -> List[Dict]:
540
- """
541
- Get all videos for a specific category
542
-
543
- Args:
544
- category: Video category to filter by
545
-
546
- Returns:
547
- List of dictionaries containing videos in that category
548
- """
549
- return [v for v in self.video_library if v.get('category') == category]
550
-
551
- def get_available_categories(self) -> List[str]:
552
- """Get list of all available categories"""
553
- categories = set(v.get('category', '') for v in self.video_library)
554
- return sorted([c for c in categories if c])
555
-
556
- def search_videos(self, keyword: str) -> List[Dict]:
557
- """
558
- Search videos by keyword in filename or description
559
-
560
- Args:
561
- keyword: Search term
562
-
563
- Returns:
564
- List of dictionaries containing matching videos
565
- """
566
- keyword_lower = keyword.lower()
567
- results = []
568
-
569
- for video in self.video_library:
570
- filename = video.get('VIDEO_FILENAME', '').lower()
571
- short_desc = video.get('short_description', '').lower()
572
- desc = video.get('description', '').lower()
573
-
574
- if keyword_lower in filename or keyword_lower in short_desc or keyword_lower in desc:
575
- results.append(video)
576
-
577
- return results
578
-
579
-
580
- def add_folder_name_column(input_csv, output_csv, downloader):
581
- processed = set()
582
-
583
- # Load already processed video filenames
584
- if os.path.exists(output_csv):
585
- with open(output_csv, newline="", encoding="utf-8") as f:
586
- reader = csv.DictReader(f)
587
- for row in reader:
588
- processed.add(row["VIDEO_FILENAME"])
589
-
590
- with open(input_csv, newline="", encoding="utf-8") as infile, \
591
- open(output_csv, "a", newline="", encoding="utf-8") as outfile:
592
-
593
- reader = csv.DictReader(infile)
594
- writer = csv.writer(outfile)
595
-
596
- # Write header only if file is new
597
- if outfile.tell() == 0:
598
- writer.writerow(["folder_name"] + reader.fieldnames)
599
-
600
- for row in reader:
601
- video_filename = row["VIDEO_FILENAME"]
602
-
603
- # Skip if already processed
604
- if video_filename in processed:
605
- continue
606
-
607
- folder_name = downloader.get_folder_name(video_filename)
608
-
609
- # 🚫 Skip if folder_name is empty / None / whitespace
610
- if not folder_name or not str(folder_name).strip():
611
- continue
612
-
613
- writer.writerow([folder_name] + list(row.values()))
614
- outfile.flush()
615
-
616
-
617
- def add_link_column(input_csv, output_csv, downloader):
618
- """Reads input_csv and writes to output_csv with an added 'video_link' column"""
619
- processed = set()
620
-
621
- # 1. Load already processed filenames to allow resuming
622
- if os.path.exists(output_csv):
623
- with open(output_csv, newline="", encoding="utf-8") as f:
624
- reader = csv.DictReader(f)
625
- if reader.fieldnames and "VIDEO_FILENAME" in reader.fieldnames:
626
- for row in reader:
627
- processed.add(row["VIDEO_FILENAME"].split("/")[-1])
628
-
629
- # 2. Process the files
630
- with open(input_csv, newline="", encoding="utf-8") as infile:
631
- reader = csv.DictReader(infile)
632
- fieldnames = reader.fieldnames
633
-
634
- # Determine if we need to write the header
635
- file_exists = os.path.exists(output_csv) and os.path.getsize(output_csv) > 0
636
-
637
- with open(output_csv, "a", newline="", encoding="utf-8") as outfile:
638
- # We want 'video_link' to be the first column
639
- writer = csv.DictWriter(outfile, fieldnames=["video_link"] + fieldnames)
640
-
641
- if not file_exists:
642
- writer.writeheader()
643
-
644
- for row in reader:
645
- video_filename = row["VIDEO_FILENAME"].split("/")[-1]
646
-
647
- if video_filename in processed:
648
- continue
649
-
650
- logger.info(f"Fetching link for: {video_filename}")
651
- video_link = downloader.get_video_link(video_filename)
652
-
653
- if not video_link:
654
- logger.warning(f"Could not find link for {video_filename}")
655
- continue
656
-
657
- # Prepare new row
658
- new_row = {"video_link": video_link}
659
- new_row.update(row)
660
-
661
- writer.writerow(new_row)
662
- outfile.flush() # Ensure it saves frequently
663
- processed.add(video_filename)
664
-
665
-
666
- # Example usage
667
- if __name__ == "__main__":
668
- try:
669
- from dotenv import load_dotenv
670
- load_dotenv()
671
-
672
- downloader = VideoDownloader()
673
- downloader._init_drive_downloader(download_path="testData/video_for_workflow")
674
- add_link_column("testData/infloxa_copy/videos.csv", "testData/infloxa_copy/videos_with_links.csv", downloader)
675
-
676
- # Download from Drive folder link
677
- # result = downloader.download_from_drive_link(
678
- # drive_link="https://drive.google.com/drive/folders/1WSrVAyqvPJzpRnoUxkNx0LqK9VlDs432",
679
- # download_root="testData/video_for_workflow",
680
- # file_extensions=['.mp4', '.mov', '.avi', '.mkv'] # Only video files
681
- # )
682
-
683
- # print(f"\nDownload completed!")
684
- # print(f"Total: {result['total_files']}, Downloaded: {result['downloaded']}, "
685
- # f"Skipped: {result['skipped']}, Failed: {result['failed']}")
686
-
687
- # paths = [
688
- # "testData/infloxa_copy/Infloxa_ Lifestyle_125videos",
689
- # "testData/infloxa_copy/Infloxa_LuxuryCars_125videos",
690
- # "testData/infloxa_copy/Infloxa_LuxuryItems_125videos",
691
- # "testData/infloxa_copy/Infloxa_LuxuryRealEstate_125videos",
692
- # "testData/infloxa_copy/Infloxa_Models_125videos",
693
- # "testData/infloxa_copy/Infloxa_PrivateJets_125videos",
694
- # "testData/infloxa_copy/Infloxa_Wealth&Exclusivity_125videos",
695
- # "testData/infloxa_copy/Infloxa_Yachts_125videos",
696
- # ]
697
-
698
- # output_csv = "testData/infloxa_copy/videos_with_links.csv"
699
-
700
- # VIDEO_EXTENSIONS = {".mp4", ".mov", ".mkv", ".avi", ".webm"}
701
-
702
- # rows = []
703
-
704
- # for base_path in paths:
705
- # base_path = Path(base_path)
706
-
707
- # if not base_path.exists():
708
- # print(f"Skipping missing folder: {base_path}")
709
- # continue
710
-
711
- # for file in base_path.iterdir():
712
- # if file.is_file() and file.suffix.lower() in VIDEO_EXTENSIONS:
713
- # rows.append([file.name])
714
-
715
- # # Write CSV
716
- # with open(output_csv, "w", newline="", encoding="utf-8") as f:
717
- # writer = csv.writer(f)
718
- # writer.writerow(["VIDEO_FILENAME"])
719
- # writer.writerows(rows)
720
-
721
- # print(f"CSV created with {len(rows)} entries → {output_csv}")
722
-
723
- except KeyboardInterrupt:
724
- print("\nStopped by Ctrl+C")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
video_analyser/.gitignore DELETED
@@ -1,25 +0,0 @@
1
- # Google OAuth (now using environment variables)
2
- # credentials.json and token.json no longer needed
3
-
4
- # Downloaded videos
5
- infloxa/
6
-
7
- # Logs
8
- *.log
9
-
10
- # Python
11
- __pycache__/
12
- *.py[cod]
13
- *$py.class
14
- *.so
15
- .Python
16
- env/
17
- venv/
18
- ENV/
19
-
20
- # OS
21
- .DS_Store
22
- Thumbs.db
23
-
24
- analyse.py
25
- dedupe_videos.sh
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
video_analyser/README.md DELETED
@@ -1,264 +0,0 @@
1
- # Luxury B-Roll Video Analysis System
2
-
3
- A comprehensive Python system for downloading, analyzing, scoring, and cataloging luxury b-roll videos for short-form social media content (Instagram Reels, TikTok, YouTube Shorts).
4
-
5
- ## Features
6
-
7
- - **Google Drive Integration**: Download entire folder structures automatically
8
- - **AI-Powered Analysis**: Uses Google Gemini AI for intelligent video scoring
9
- - **Technical Analysis**: Motion detection, face detection, duration extraction
10
- - **Weighted Scoring**: Configurable multi-factor scoring system
11
- - **CSV Output**: Machine-readable catalog for automated video selection
12
- - **Progress Tracking**: Real-time progress bars and detailed logging
13
-
14
- ## System Requirements
15
-
16
- - Python 3.8+
17
- - OpenCV (for video processing)
18
- - Google Cloud credentials (for Drive API)
19
- - Gemini API key (for AI analysis)
20
- - Sufficient disk space (50GB+ recommended)
21
-
22
- ## Installation
23
-
24
- ### 1. Install Dependencies
25
-
26
- ```bash
27
- cd video_analyser
28
- pip install -r requirements.txt
29
- ```
30
-
31
- ### 2. Setup Google Drive API
32
-
33
- 1. Go to [Google Cloud Console](https://console.cloud.google.com/)
34
- 2. Create a new project (or select existing)
35
- 3. Enable the **Google Drive API**
36
- 4. Create OAuth 2.0 credentials:
37
- - Go to **Credentials** → **Create Credentials** → **OAuth client ID**
38
- - Application type: **Desktop app**
39
- - Download the credentials
40
- 5. Save as `credentials.json` in the `video_analyser/` directory
41
-
42
- ### 3. Setup Gemini API
43
-
44
- 1. Get your Gemini API key from [Google AI Studio](https://makersuite.google.com/app/apikey)
45
- 2. Set environment variable:
46
-
47
- ```bash
48
- export GEMINI_API_KEY='your-api-key-here'
49
-
50
- # For permanent setup, add to ~/.bashrc or ~/.zshrc:
51
- echo 'export GEMINI_API_KEY="your-api-key-here"' >> ~/.bashrc
52
- source ~/.bashrc
53
- ```
54
-
55
- ### 4. Customize Analysis Prompt (Optional)
56
-
57
- The AI analysis prompt is stored in `analysis_prompt.md`. You can edit this file to customize:
58
- - Scoring criteria
59
- - Description style
60
- - Quality guidelines
61
- - Output format
62
-
63
- ### 4. Configure Settings
64
-
65
- Edit `config.yaml` to customize:
66
- - Google Drive folder ID (default is already set)
67
- - Processing settings (concurrent downloads, frame sampling)
68
- - Scoring weights
69
- - Output paths
70
-
71
- ## Usage
72
-
73
- ### First Run (Full Pipeline)
74
-
75
- ```bash
76
- python main.py
77
- ```
78
-
79
- This will:
80
- 1. Authenticate with Google Drive (opens browser)
81
- 2. Download all videos maintaining folder structure
82
- 3. Analyze each video with AI
83
- 4. Generate `infloxa_video_analysis.csv`
84
-
85
- ### Skip Download (Process Existing Videos)
86
-
87
- If you already have videos downloaded:
88
-
89
- ```bash
90
- python main.py --skip-download
91
- ```
92
-
93
- ### Test Mode (Process First 3 Videos)
94
-
95
- For testing the pipeline:
96
-
97
- ```bash
98
- python main.py --test-mode
99
- ```
100
-
101
- ### Custom Config File
102
-
103
- ```bash
104
- python main.py --config custom_config.yaml
105
- ```
106
-
107
- ## Output Format
108
-
109
- The system generates `infloxa_video_analysis.csv` with the following columns:
110
-
111
- | Column | Type | Description |
112
- |--------|------|-------------|
113
- | `video_filename` | string | Original filename |
114
- | `category` | string | Folder name (e.g., luxury_car, yacht) |
115
- | `duration_seconds` | float | Video duration |
116
- | `description` | string | AI-generated luxury description (detailed) |
117
- | `luxury_score` | int (0-100) | Exclusivity and premium feel |
118
- | `visual_clarity_score` | int (0-100) | Composition and mobile readability |
119
- | `motion_score` | int (0-100) | Camera/subject motion level |
120
- | `beat_editing_score` | int (0-100) | Suitability for beat-synced edits |
121
- | `recommended_edit_type` | enum | static_hold, slow_pan, beat_cut, pulse_cut, transition_clip |
122
- | `ideal_clip_length_seconds` | float | Recommended clip length (typically 1.0-2.5s) |
123
- | `energy_level` | enum | low, medium, high |
124
- | `has_text_or_watermark` | boolean | true/false |
125
- | `has_faces_visible` | boolean | true/false |
126
- | `usable_for_ads` | boolean | Commercial safety (true/false) |
127
- | `final_selection_score` | int (0-100) | Weighted overall score |
128
-
129
- ## Project Structure
130
-
131
- ```
132
- video_analyser/
133
- ├── config.yaml # Configuration settings
134
- ├── requirements.txt # Python dependencies
135
- ├── README.md # This file
136
- ├── analysis_prompt.md # Gemini AI analysis prompt
137
- ├── credentials.json # Google Drive credentials (you provide)
138
- ├── token.json # Auto-generated OAuth token
139
- ├── main.py # Main orchestrator
140
- ├── video_analysis.log # Execution logs
141
- ├── modules/
142
- │ ├── __init__.py
143
- │ ├── drive_downloader.py # Google Drive integration
144
- │ ├── video_analyzer.py # Technical video analysis
145
- │ ├── ai_analyzer.py # Gemini AI analysis
146
- │ ├── scorer.py # Scoring algorithms
147
- │ └── csv_writer.py # CSV output handler
148
- ├── infloxa/ # Downloaded videos (auto-created)
149
- │ ├── category_1/
150
- │ ├── category_2/
151
- │ └── ...
152
- └── infloxa_video_analysis.csv # Output CSV
153
- ```
154
-
155
- ## Workflow Pipeline
156
-
157
- ```
158
- 1. Download Videos (Google Drive)
159
-
160
- 2. Extract Technical Metadata (duration, motion, faces)
161
-
162
- 3. Sample Key Frames
163
-
164
- 4. AI Analysis with Gemini (descriptions, luxury scoring)
165
-
166
- 5. Calculate Weighted Scores
167
-
168
- 6. Write Row to CSV
169
- ```
170
-
171
- ## Scoring Algorithm
172
-
173
- The `final_selection_score` is calculated using weighted factors:
174
-
175
- ```python
176
- final_score = (
177
- luxury_score × 0.40 +
178
- visual_clarity_score × 0.25 +
179
- beat_editing_score × 0.20 +
180
- motion_score × 0.10 +
181
- usability_score × 0.05
182
- )
183
- ```
184
-
185
- Weights can be customized in `config.yaml`.
186
-
187
- ## Logging
188
-
189
- Detailed logs are written to `video_analysis.log`. Log level can be adjusted in `config.yaml`:
190
- - `DEBUG`: Verbose output for troubleshooting
191
- - `INFO`: Standard operational messages
192
- - `WARNING`: Important warnings
193
- - `ERROR`: Error messages only
194
-
195
- ## Troubleshooting
196
-
197
- ### Google Drive Authentication Issues
198
-
199
- - Delete `token.json` and re-authenticate
200
- - Ensure `credentials.json` is valid OAuth 2.0 credentials
201
- - Check that Drive API is enabled in Google Cloud Console
202
-
203
- ### Gemini API Errors
204
-
205
- - Verify `GEMINI_API_KEY` environment variable is set: `echo $GEMINI_API_KEY`
206
- - Check you have sufficient quota/credits
207
- - Ensure `google-generativeai` package is up to date
208
-
209
- ### Video Processing Errors
210
-
211
- - Ensure videos are in supported formats (mp4, mov, avi, mkv, webm)
212
- - Check that OpenCV is properly installed
213
- - For face detection issues, install `opencv-contrib-python`
214
-
215
- ### Out of Memory
216
-
217
- - Reduce `frames_to_sample` in `config.yaml`
218
- - Process videos in smaller batches
219
- - Close other applications
220
-
221
- ## Advanced Usage
222
-
223
- ### Resume After Interruption
224
-
225
- The system automatically skips:
226
- - Already downloaded videos
227
- - Videos already in CSV (use append mode)
228
-
229
- Simply re-run `python main.py` to continue.
230
-
231
- ### Batch Processing
232
-
233
- Edit `config.yaml` to adjust:
234
- ```yaml
235
- processing:
236
- max_concurrent_downloads: 3 # Parallel downloads
237
- batch_size: 10 # Process in batches
238
- ```
239
-
240
- ### Custom Scoring Weights
241
-
242
- Modify weights in `config.yaml` (must sum to 1.0):
243
- ```yaml
244
- scoring:
245
- luxury_weight: 0.40
246
- visual_clarity_weight: 0.25
247
- beat_editing_weight: 0.20
248
- motion_weight: 0.10
249
- usability_weight: 0.05
250
- ```
251
-
252
- ## API Costs
253
-
254
- **Gemini API**: Each video analysis sends 3 frames (configurable). Monitor your usage at [Google AI Studio](https://makersuite.google.com/).
255
-
256
- **Google Drive API**: Free tier includes 10,000 requests/day (sufficient for most use cases).
257
-
258
- ## License
259
-
260
- This is a proprietary system for Infloxa luxury video analysis.
261
-
262
- ## Support
263
-
264
- For issues or questions, check the logs in `video_analysis.log` and ensure all configuration is correct.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
video_analyser/analysis_prompt.md DELETED
@@ -1,246 +0,0 @@
1
- **System / Instruction Prompt**
2
-
3
- You are analyzing **luxury b-roll stock footage** for short-form social media content (Instagram Reels, TikTok, YouTube Shorts).
4
-
5
- This footage is sold as **premium luxury stock video** and will be used by:
6
-
7
- * Luxury & motivation theme pages
8
- * Content creators and agencies
9
- * Beat-synced and pulse-based edits
10
- * Premium brand advertising
11
-
12
- Your goal is to **label, score, and describe** each video so it can be efficiently selected and edited at scale.
13
-
14
- ---
15
-
16
- ## 🎥 VIDEO CONTEXT
17
-
18
- Each input is a **short luxury b-roll clip** (no dialogue).
19
- Assume the video may be used for:
20
-
21
- * Beat-based editing
22
- * Pulse-based editing
23
- * Static luxury holds
24
- * Fast-paced aesthetic reels
25
-
26
- ---
27
-
28
- ## 🧠 ANALYSIS REQUIREMENTS
29
-
30
- Analyze the video and return **ONLY valid JSON** with the following fields.
31
-
32
- ---
33
-
34
- ### 1️⃣ category (string)
35
-
36
- Choose **ONE** primary category that best represents the clip:
37
-
38
- * `luxury_architecture`
39
- * `luxury_car`
40
- * `yacht_boat`
41
- * `private_jet`
42
- * `helicopter`
43
- * `luxury_watch`
44
- * `jewelry_accessories`
45
- * `luxury_fashion`
46
- * `fine_dining`
47
- * `champagne_wine`
48
- * `luxury_hotel`
49
- * `infinity_pool`
50
- * `penthouse_interior`
51
- * `cityscape`
52
- * `skyline_night`
53
- * `luxury_lifestyle`
54
- * `private_estate`
55
- * `golf_course`
56
- * `casino_gaming`
57
- * `luxury_tech`
58
- * `supercars_racing`
59
- * `nature_premium`
60
- * `tropical_beach`
61
- * `mountain_resort`
62
- * `desert_luxury`
63
- * `abstract_luxury`
64
- * `gold_elements`
65
- * `money_cash`
66
- * `crypto_finance`
67
- * `art_gallery`
68
- * `luxury_retail`
69
- * `other`
70
-
71
- ---
72
-
73
- ### 2️⃣ short_description (string)
74
-
75
- A **concise one-line summary** (10-15 words max) of what the video shows.
76
-
77
- **Examples**:
78
- - "Luxury yacht sailing at sunset"
79
- - "Modern penthouse with city skyline view"
80
- - "Sports car driving through mountain road"
81
-
82
- ---
83
-
84
- ### 3️⃣ description (string)
85
-
86
- Write a **high-quality, premium description** (3–5 sentences) that explains:
87
-
88
- * What is visible in the video
89
- * Why it feels luxurious or premium
90
- * Camera movement or framing style
91
- * Mood and atmosphere
92
- * Why it works for luxury content
93
-
94
- Focus on **visual storytelling**, not generic stock descriptions.
95
-
96
- ---
97
-
98
- ### 4️⃣ luxury_score (integer 0–100)
99
-
100
- Rate the **exclusivity and aspirational quality**.
101
-
102
- **Be strict**:
103
-
104
- * Average clips should NOT exceed 70
105
- * Only truly exceptional luxury clips should score above 85
106
-
107
- ---
108
-
109
- ### 5️⃣ visual_clarity_score (integer 0–100)
110
-
111
- Rate how well the clip works on **mobile screens**:
112
-
113
- * Clear subject
114
- * Clean composition
115
- * Not overly busy
116
- * Professional framing
117
-
118
- ---
119
-
120
- ### 6️⃣ motion_score (integer 0–100)
121
-
122
- Rate the **amount and quality of motion**:
123
-
124
- * Camera movement
125
- * Subject movement
126
- * Suitability for dynamic edits
127
-
128
- ---
129
-
130
- ### 7️⃣ beat_editing_score (integer 0–100)
131
-
132
- How well does this clip support:
133
-
134
- * Beat-based editing
135
- * Pulse-based rhythm
136
- * Fast luxury reels
137
-
138
- ---
139
-
140
- ### 8️⃣ recommended_edit_type (string)
141
-
142
- Choose **ONE**:
143
-
144
- * `static_hold` – Minimal motion, elegant pause moments
145
- * `slow_pan` – Smooth cinematic movement
146
- * `beat_cut` – Sharp, intentional cuts on beats
147
- * `pulse_cut` – Rhythmic pulsing edits
148
- * `transition_clip` – Works well between scenes
149
-
150
- ---
151
-
152
- ### 9️⃣ ideal_clip_length_seconds (float)
153
-
154
- Best usable duration for short-form editing
155
- (typically **1.0–2.5 seconds**).
156
-
157
- ---
158
-
159
- ### 🔟 energy_level (string)
160
-
161
- Choose **ONE**:
162
-
163
- * `low` – Calm, slow, serene
164
- * `medium` – Balanced pacing
165
- * `high` – Fast, energetic, dynamic
166
-
167
- ---
168
-
169
- ### 1️⃣1️⃣ has_text_or_watermark (boolean)
170
-
171
- Does the clip contain **any visible text, logos, or watermarks**?
172
-
173
- ---
174
-
175
- ### 1️⃣2️⃣ usable_for_ads (boolean)
176
-
177
- Is this clip **commercially safe**?
178
-
179
- * False if logos, brands, text, or legal risks are visible
180
-
181
- ---
182
-
183
- ### 1️⃣3️⃣ final_selection_score (integer 0–100)
184
-
185
- Overall suitability for **Infloxa luxury content**, considering:
186
-
187
- * Luxury quality
188
- * Visual clarity
189
- * Motion
190
- * Beat usability
191
- * Commercial safety
192
-
193
- This score will be used to **rank and select the best clips**.
194
-
195
- ---
196
-
197
- ## 📤 OUTPUT FORMAT (STRICT)
198
-
199
- **Output ONLY valid JSON.**
200
- No explanations.
201
- No markdown.
202
- No extra text.
203
-
204
- ```json
205
- {
206
- "category": "luxury_architecture",
207
- "short_description": "Futuristic yacht with glass garden at twilight",
208
- "description": "Detailed 3–5 sentence luxury description...",
209
- "luxury_score": 0,
210
- "visual_clarity_score": 0,
211
- "motion_score": 0,
212
- "beat_editing_score": 0,
213
- "recommended_edit_type": "slow_pan",
214
- "ideal_clip_length_seconds": 1.5,
215
- "energy_level": "medium",
216
- "has_text_or_watermark": false,
217
- "usable_for_ads": true,
218
- "final_selection_score": 0
219
- }
220
- ```
221
-
222
- ---
223
-
224
- ## 🎯 SCORING GUIDELINES
225
-
226
- **Luxury Score Calibration**
227
-
228
- * 0–30: Not luxury
229
- * 31–50: Mild premium feel
230
- * 51–70: Good luxury content
231
- * 71–85: Exceptional
232
- * 86–100: Rare, top-tier
233
-
234
- **Penalize**:
235
-
236
- * Generic stock footage
237
- * Weak luxury signals
238
- * Overly busy or unclear visuals
239
- * Poor framing
240
-
241
- **Favor**:
242
-
243
- * Premium mobile aesthetics
244
- * Clean looping potential
245
- * Strong beat compatibility
246
- * High aspirational value
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
video_analyser/config.yaml DELETED
@@ -1,39 +0,0 @@
1
- # Luxury Video Analysis System Configuration
2
-
3
- # Google Drive Settings
4
- google_drive:
5
- folder_id: "1WSrVAyqvPJzpRnoUxkNx0LqK9VlDs432"
6
- # OAuth credentials from environment variables:
7
- # - SERVER_GOOGLE_CLIENT_ID
8
- # - SERVER_GOOGLE_CLIENT_SECRET
9
- # - SERVER_GOOGLE_REFRESH_TOKEN
10
-
11
- # Gemini API Settings
12
- gemini:
13
- # API key is read from GEMINI_API_KEY environment variable
14
- model: "gemini-3-pro-preview" # or "gemini-1.5-pro" for better quality
15
- prompt_file: "analysis_prompt.md" # Path to analysis prompt
16
-
17
- # Processing Settings
18
- processing:
19
- max_concurrent_downloads: 3
20
- batch_size: 10 # Process videos in batches
21
- frames_to_sample: 3 # Number of frames to send to AI
22
-
23
- # Output Settings
24
- output:
25
- local_video_dir: "video_for_workflow" # Relative to video_analyser/
26
- csv_file: "infloxa_video_analysis.csv" # Relative to video_analyser/
27
-
28
- # Scoring Weights (must sum to 1.0)
29
- scoring:
30
- luxury_weight: 0.40
31
- visual_clarity_weight: 0.25
32
- beat_editing_weight: 0.20
33
- motion_weight: 0.10
34
- usability_weight: 0.05
35
-
36
- # Logging
37
- logging:
38
- level: "INFO" # DEBUG, INFO, WARNING, ERROR
39
- file: "video_analysis.log" # Relative to video_analyser/
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
video_analyser/get_refresh_token.py DELETED
@@ -1,21 +0,0 @@
1
- from google_auth_oauthlib.flow import InstalledAppFlow
2
-
3
- # ✅ Use YouTube scope (not Drive)
4
- SCOPES = ['https://www.googleapis.com/auth/drive.readonly']
5
-
6
- def main():
7
- print("🔑 Starting OAuth flow...")
8
-
9
- flow = InstalledAppFlow.from_client_secrets_file(
10
- "whoa/client_secret_688373610660-vtr5l8q7s4is9kkvd7hla1cqg273emfs.apps.googleusercontent.com.json",
11
- SCOPES
12
- )
13
-
14
- creds = flow.run_local_server(port=0)
15
-
16
- print("\n✅ AUTH SUCCESS")
17
- print("REFRESH TOKEN:\n")
18
- print(creds.refresh_token)
19
-
20
- if __name__ == "__main__":
21
- main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
video_analyser/infloxa_video_analysis.csv DELETED
The diff for this file is too large to render. See raw diff
 
video_analyser/infloxa_video_analysis_with_folders.csv DELETED
The diff for this file is too large to render. See raw diff
 
video_analyser/main.py DELETED
@@ -1,501 +0,0 @@
1
- """
2
- Luxury B-Roll Video Analysis System
3
- Main orchestrator for downloading, analyzing, scoring, and cataloging luxury videos
4
- """
5
-
6
- import os
7
- import sys
8
- import yaml
9
- import logging
10
- import argparse
11
- from pathlib import Path
12
- from typing import Dict, List
13
- from tqdm import tqdm
14
- from dotenv import load_dotenv
15
-
16
- # Load environment variables from .env file
17
- load_dotenv()
18
-
19
- from modules import (
20
- DriveDownloader,
21
- VideoAnalyzer,
22
- AIAnalyzer,
23
- Scorer,
24
- CSVWriter
25
- )
26
- from modules.git_ops import git_commit_progress
27
-
28
-
29
- # Get script directory for relative paths
30
- SCRIPT_DIR = Path(__file__).parent.absolute()
31
- PROGRESS_DIR = SCRIPT_DIR / "progress"
32
-
33
-
34
- def get_progress_file(job_index=None):
35
- """Get the appropriate progress file for this job."""
36
- PROGRESS_DIR.mkdir(exist_ok=True)
37
-
38
- if job_index is not None:
39
- return PROGRESS_DIR / f"analyzed_videos_job{job_index}.txt"
40
- return PROGRESS_DIR / "analyzed_videos.txt"
41
-
42
-
43
- def load_all_analyzed_videos():
44
- """
45
- Load analyzed videos from all job-specific progress files.
46
- Returns a set of video filenames that have been successfully analyzed.
47
- """
48
- analyzed = set()
49
-
50
- if not PROGRESS_DIR.exists():
51
- return analyzed
52
-
53
- # Load from main progress file
54
- main_progress = PROGRESS_DIR / "analyzed_videos.txt"
55
- if main_progress.exists():
56
- with open(main_progress, "r") as f:
57
- analyzed.update(x.strip() for x in f if x.strip())
58
-
59
- # Load from all job-specific files
60
- for job_file in PROGRESS_DIR.glob("analyzed_videos_job*.txt"):
61
- with open(job_file, "r") as f:
62
- analyzed.update(x.strip() for x in f if x.strip())
63
-
64
- return analyzed
65
-
66
-
67
- def setup_logging(config: dict):
68
- """
69
- Setup logging configuration
70
-
71
- Args:
72
- config: Configuration dictionary
73
- """
74
- log_level = getattr(logging, config['logging']['level'].upper())
75
- log_file = config['logging']['file']
76
-
77
- # Create formatter
78
- formatter = logging.Formatter(
79
- '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
80
- )
81
-
82
- # File handler
83
- file_handler = logging.FileHandler(log_file)
84
- file_handler.setLevel(log_level)
85
- file_handler.setFormatter(formatter)
86
-
87
- # Console handler
88
- console_handler = logging.StreamHandler()
89
- console_handler.setLevel(logging.INFO)
90
- console_handler.setFormatter(formatter)
91
-
92
- # Root logger
93
- root_logger = logging.getLogger()
94
- root_logger.setLevel(log_level)
95
- root_logger.addHandler(file_handler)
96
- root_logger.addHandler(console_handler)
97
-
98
-
99
- def load_config(config_path: str = 'config.yaml') -> dict:
100
- """
101
- Load configuration from YAML file
102
-
103
- Args:
104
- config_path: Path to config file (relative to script directory or absolute)
105
-
106
- Returns:
107
- Configuration dictionary
108
- """
109
- try:
110
- # Get the directory where this script is located
111
- script_dir = Path(__file__).parent.absolute()
112
-
113
- # If config_path is just a filename, look in script directory
114
- config_file = Path(config_path)
115
- if not config_file.is_absolute() and config_file.name == config_path:
116
- config_file = script_dir / config_path
117
-
118
- with open(config_file, 'r') as f:
119
- config = yaml.safe_load(f)
120
- return config
121
- except Exception as e:
122
- print(f"Error loading config: {e}")
123
- sys.exit(1)
124
-
125
-
126
- def validate_config(config: dict) -> bool:
127
- """
128
- Validate configuration has all required fields
129
-
130
- Args:
131
- config: Configuration dictionary
132
-
133
- Returns:
134
- True if valid, False otherwise
135
- """
136
- # Check Gemini API key from environment
137
- if not os.getenv('GEMINI_API_KEY'):
138
- print("ERROR: GEMINI_API_KEY environment variable not set")
139
- print("Set it with: export GEMINI_API_KEY='your-api-key-here'")
140
- return False
141
-
142
- # Check Google OAuth credentials from environment
143
- required_oauth = [
144
- 'SERVER_GOOGLE_CLIENT_ID',
145
- 'SERVER_GOOGLE_CLIENT_SECRET',
146
- 'SERVER_GOOGLE_REFRESH_TOKEN'
147
- ]
148
-
149
- missing_oauth = [var for var in required_oauth if not os.getenv(var)]
150
- if missing_oauth:
151
- print("ERROR: Missing Google OAuth environment variables:")
152
- for var in missing_oauth:
153
- print(f" - {var}")
154
- print("\nSet them with:")
155
- print(" export SERVER_GOOGLE_CLIENT_ID='your-client-id'")
156
- print(" export SERVER_GOOGLE_CLIENT_SECRET='your-client-secret'")
157
- print(" export SERVER_GOOGLE_REFRESH_TOKEN='your-refresh-token'")
158
- return False
159
-
160
- return True
161
-
162
-
163
- def process_single_video(video_path: str, config: dict,
164
- video_analyzer: VideoAnalyzer,
165
- ai_analyzer: AIAnalyzer,
166
- scorer: Scorer,
167
- csv_writer: CSVWriter,
168
- progress_file: Path,
169
- job_index: int = None,
170
- commit: bool = False) -> bool:
171
- """
172
- Process a single video through the complete pipeline
173
-
174
- Args:
175
- video_path: Path to video file
176
- config: Configuration dictionary
177
- video_analyzer: VideoAnalyzer instance
178
- ai_analyzer: AIAnalyzer instance
179
- scorer: Scorer instance
180
- csv_writer: CSVWriter instance
181
- progress_file: Path to progress tracking file
182
- job_index: Job index for parallel processing
183
- commit: Whether to commit progress to git
184
-
185
- Returns:
186
- True if successful, False otherwise
187
- """
188
- logger = logging.getLogger(__name__)
189
- video_filename = os.path.basename(video_path)
190
-
191
- try:
192
- # STEP 1: Technical Analysis
193
- logger.info(f"Processing: {video_filename}")
194
- technical_analysis = video_analyzer.analyze_video(video_path)
195
-
196
- # STEP 2: AI Analysis
197
- ai_analysis = ai_analyzer.analyze_with_gemini(
198
- video_path,
199
- video_filename
200
- )
201
-
202
- if not ai_analysis:
203
- logger.error(f"AI analysis failed for {video_path}")
204
- return False
205
-
206
- # STEP 3: Calculate Scores
207
- scoring_results = scorer.calculate_final_score(
208
- technical_analysis,
209
- ai_analysis
210
- )
211
-
212
- if not scoring_results:
213
- logger.error(f"Scoring failed for {video_path}")
214
- return False
215
-
216
- # STEP 4: Write to CSV
217
- csv_writer.write_row(
218
- video_path,
219
- technical_analysis,
220
- ai_analysis,
221
- scoring_results,
222
- config['output']['local_video_dir']
223
- )
224
-
225
- # STEP 5: Track progress
226
- with progress_file.open("a") as pf:
227
- pf.write(f"{video_filename}\n")
228
-
229
- logger.info(
230
- f"✓ Completed: {video_filename} "
231
- f"(Luxury: {scoring_results['luxury_score']}, "
232
- f"Final: {scoring_results['final_selection_score']})"
233
- )
234
-
235
- # STEP 6: Commit progress (if enabled)
236
- if commit:
237
- git_commit_progress(
238
- config['output']['csv_file'],
239
- config['logging']['file'],
240
- progress_file,
241
- job_index,
242
- commit
243
- )
244
-
245
- return True
246
-
247
- except Exception as e:
248
- logger.error(f"Error processing {video_path}: {e}", exc_info=True)
249
- return False
250
-
251
-
252
- def get_local_videos(local_dir: str) -> List[str]:
253
- """
254
- Get list of all video files in local directory
255
-
256
- Args:
257
- local_dir: Base directory to search
258
-
259
- Returns:
260
- List of video file paths
261
- """
262
- video_extensions = ['.mp4', '.mov', '.avi', '.mkv', '.webm']
263
- video_files = []
264
-
265
- for root, dirs, files in os.walk(local_dir):
266
- for file in files:
267
- if any(file.lower().endswith(ext) for ext in video_extensions):
268
- video_files.append(os.path.join(root, file))
269
-
270
- return sorted(video_files)
271
-
272
-
273
- def main():
274
- """Main execution flow with progress tracking and parallel job support"""
275
- parser = argparse.ArgumentParser(
276
- description='Luxury B-Roll Video Analysis System',
277
- formatter_class=argparse.RawDescriptionHelpFormatter,
278
- epilog="""
279
- Examples:
280
- # Run without committing
281
- python main.py
282
-
283
- # Run and auto-commit progress
284
- python main.py --commit
285
-
286
- # Skip download, process existing videos
287
- python main.py --skip-download --commit
288
-
289
- # Parallel processing (job 1 of 3)
290
- python main.py --commit --job-index 0 --total-jobs 3
291
-
292
- # Test mode
293
- python main.py --test-mode
294
- """
295
- )
296
- parser.add_argument(
297
- '--skip-download',
298
- action='store_true',
299
- help='Skip Google Drive download and process existing local videos'
300
- )
301
- parser.add_argument(
302
- '--test-mode',
303
- action='store_true',
304
- help='Process only first 3 videos for testing'
305
- )
306
- parser.add_argument(
307
- '--config',
308
- default='config.yaml',
309
- help='Path to configuration file'
310
- )
311
- parser.add_argument(
312
- '--commit',
313
- action='store_true',
314
- help='Commit and push results to git repository'
315
- )
316
- parser.add_argument(
317
- '--job-index',
318
- type=int,
319
- default=None,
320
- help='Index of this job (0-based) for parallel processing'
321
- )
322
- parser.add_argument(
323
- '--total-jobs',
324
- type=int,
325
- default=None,
326
- help='Total number of parallel jobs'
327
- )
328
-
329
- args = parser.parse_args()
330
-
331
- # Allow environment variables to override args (for GitHub Actions)
332
- job_index = args.job_index
333
- total_jobs = args.total_jobs
334
-
335
- if job_index is None and "JOB_INDEX" in os.environ:
336
- job_index = int(os.environ["JOB_INDEX"])
337
- if total_jobs is None and "TOTAL_JOBS" in os.environ:
338
- total_jobs = int(os.environ["TOTAL_JOBS"])
339
-
340
- # Load configuration
341
- print("Loading configuration...")
342
- config = load_config(args.config)
343
-
344
- # Make paths relative to video_analyser directory
345
- script_dir = Path(__file__).parent.absolute()
346
- if not Path(config['output']['local_video_dir']).is_absolute():
347
- config['output']['local_video_dir'] = str(script_dir / config['output']['local_video_dir'])
348
- if not Path(config['output']['csv_file']).is_absolute():
349
- config['output']['csv_file'] = str(script_dir / config['output']['csv_file'])
350
- if not Path(config['logging']['file']).is_absolute():
351
- config['logging']['file'] = str(script_dir / config['logging']['file'])
352
-
353
- # Setup logging
354
- setup_logging(config)
355
- logger = logging.getLogger(__name__)
356
-
357
- logger.info("=" * 60)
358
- logger.info("Luxury B-Roll Video Analysis System")
359
- logger.info("=" * 60)
360
-
361
- # Validate configuration
362
- if not validate_config(config):
363
- sys.exit(1)
364
-
365
- # Get progress file for this job
366
- progress_file = get_progress_file(job_index)
367
-
368
- # Load all analyzed videos (from all jobs)
369
- analyzed_videos = load_all_analyzed_videos()
370
- logger.info(f"📊 Found {len(analyzed_videos)} already analyzed videos (skipping these)")
371
-
372
- # Initialize modules
373
- logger.info("Initializing modules...")
374
- video_analyzer = VideoAnalyzer(config)
375
- ai_analyzer = AIAnalyzer(config)
376
- scorer = Scorer(config)
377
- csv_writer = CSVWriter(config)
378
-
379
- # STEP 1: Get list of videos from Google Drive (don't download yet)
380
- if not args.skip_download:
381
- logger.info("STEP 1: Connecting to Google Drive")
382
- drive_downloader = DriveDownloader(config)
383
- drive_downloader.authenticate()
384
-
385
- # Get list of all videos without downloading
386
- logger.info("Fetching video list from Google Drive...")
387
- all_video_items = drive_downloader.list_all_videos()
388
- logger.info(f"Found {len(all_video_items)} videos in Google Drive")
389
-
390
- else:
391
- logger.info("STEP 1: Skipping download, using local videos")
392
- local_dir = config['output']['local_video_dir']
393
-
394
- if not os.path.exists(local_dir):
395
- logger.error(f"Local directory does not exist: {local_dir}")
396
- sys.exit(1)
397
-
398
- # Get local videos
399
- video_files = get_local_videos(local_dir)
400
- logger.info(f"Found {len(video_files)} local videos")
401
- all_video_items = [{'local_path': vf} for vf in video_files]
402
-
403
- if not all_video_items:
404
- logger.error("No videos to process!")
405
- sys.exit(1)
406
-
407
- # Filter out already analyzed videos
408
- analyzed_videos = load_all_analyzed_videos()
409
- videos_to_process = []
410
-
411
- for item in all_video_items:
412
- if 'local_path' in item:
413
- filename = os.path.basename(item['local_path'])
414
- else:
415
- filename = item['name']
416
-
417
- if filename not in analyzed_videos:
418
- videos_to_process.append(item)
419
-
420
- logger.info(
421
- f"📹 Total videos: {len(all_video_items)}, "
422
- f"Already analyzed: {len(all_video_items) - len(videos_to_process)}, "
423
- f"To process: {len(videos_to_process)}"
424
- )
425
-
426
- if not videos_to_process:
427
- logger.info("✅ All videos already analyzed!")
428
- return
429
-
430
- # STEP 2-4: Process each video ONE BY ONE
431
- logger.info(f"\nProcessing {len(videos_to_process)} videos one-by-one...\n")
432
-
433
- successful = 0
434
- failed = 0
435
-
436
- for idx, item in enumerate(videos_to_process, 1):
437
- try:
438
- # Download single video if from Drive
439
- if 'local_path' not in item:
440
- logger.info(f"[{idx}/{len(videos_to_process)}] Downloading: {item['name']}")
441
- local_path = drive_downloader.download_single_video(item)
442
- if not local_path:
443
- logger.error(f"Failed to download {item['name']}")
444
- failed += 1
445
- continue
446
- else:
447
- local_path = item['local_path']
448
-
449
- # Process the video
450
- logger.info(f"[{idx}/{len(videos_to_process)}] Analyzing: {os.path.basename(local_path)}")
451
-
452
- if process_single_video(
453
- local_path, config, video_analyzer, ai_analyzer, scorer, csv_writer,
454
- progress_file, job_index, args.commit
455
- ):
456
- successful += 1
457
- logger.info(f"✅ [{idx}/{len(videos_to_process)}] Completed successfully")
458
-
459
- # Commit after each successful video
460
- if args.commit:
461
- git_commit_progress(
462
- config['output']['csv_file'],
463
- config['logging']['file'],
464
- progress_file,
465
- job_index,
466
- args.commit
467
- )
468
- else:
469
- failed += 1
470
- logger.warning(f"⚠️ [{idx}/{len(videos_to_process)}] Processing failed")
471
-
472
- except Exception as e:
473
- logger.error(f"❌ Error processing video {idx}: {e}", exc_info=True)
474
- failed += 1
475
- continue
476
-
477
- # Final commit
478
- if args.commit and successful > 0:
479
- git_commit_progress(
480
- config['output']['csv_file'],
481
- config['logging']['file'],
482
- progress_file,
483
- job_index,
484
- args.commit
485
- )
486
-
487
- # Summary
488
- job_label = f"Job {job_index}" if job_index is not None else "Analysis"
489
- logger.info("\n" + "=" * 60)
490
- logger.info(f"{job_label} COMPLETE")
491
- logger.info("=" * 60)
492
- logger.info(f"Total videos: {len(videos_to_process)}")
493
- logger.info(f"Successful: {successful}")
494
- logger.info(f"Failed: {failed}")
495
- logger.info(f"CSV output: {config['output']['csv_file']}")
496
- logger.info(f"Progress file: {progress_file}")
497
- logger.info("=" * 60)
498
-
499
-
500
- if __name__ == '__main__':
501
- main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
video_analyser/modules/__init__.py DELETED
@@ -1,19 +0,0 @@
1
- """
2
- Luxury Video Analysis System Modules
3
- """
4
-
5
- from .drive_downloader import DriveDownloader
6
- from .video_analyzer import VideoAnalyzer
7
- from .ai_analyzer import AIAnalyzer
8
- from .scorer import Scorer
9
- from .csv_writer import CSVWriter
10
- from .git_ops import git_commit_progress
11
-
12
- __all__ = [
13
- 'DriveDownloader',
14
- 'VideoAnalyzer',
15
- 'AIAnalyzer',
16
- 'Scorer',
17
- 'CSVWriter',
18
- 'git_commit_progress'
19
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
video_analyser/modules/ai_analyzer.py DELETED
@@ -1,186 +0,0 @@
1
- """
2
- AI Analyzer Module
3
- Uses Gemini API for intelligent video analysis and scoring
4
- """
5
-
6
- import os
7
- import logging
8
- import json
9
- from typing import Dict, List, Optional
10
- from pathlib import Path
11
- import google.generativeai as genai
12
-
13
- logger = logging.getLogger(__name__)
14
-
15
-
16
- class AIAnalyzer:
17
- """Use Gemini AI to analyze luxury video content"""
18
-
19
- def __init__(self, config: dict):
20
- """
21
- Initialize AI Analyzer
22
-
23
- Args:
24
- config: Configuration dictionary with gemini settings
25
- """
26
- # Get API key from environment
27
- self.api_key = os.getenv('GEMINI_API_KEY')
28
- if not self.api_key:
29
- raise ValueError("GEMINI_API_KEY environment variable not set")
30
-
31
- self.model_name = config['gemini']['model']
32
- self.prompt_file = config['gemini']['prompt_file']
33
-
34
- # Load prompt from file
35
- self.prompt_template = self._load_prompt()
36
-
37
- # Configure Gemini
38
- genai.configure(api_key=self.api_key)
39
- self.model = genai.GenerativeModel(self.model_name)
40
-
41
- logger.info(f"Initialized Gemini AI with model: {self.model_name}")
42
-
43
- def _load_prompt(self) -> str:
44
- """Load analysis prompt from markdown file"""
45
- try:
46
- # Get path relative to this module's location
47
- module_dir = Path(__file__).parent.parent.absolute()
48
- prompt_path = Path(self.prompt_file)
49
-
50
- # If it's just a filename, look in video_analyser directory
51
- if not prompt_path.is_absolute() and prompt_path.name == self.prompt_file:
52
- prompt_path = module_dir / self.prompt_file
53
-
54
- with open(prompt_path, 'r', encoding='utf-8') as f:
55
- prompt = f.read()
56
- logger.info(f"Loaded prompt from {prompt_path}")
57
- return prompt
58
- except Exception as e:
59
- logger.error(f"Failed to load prompt file: {e}")
60
- raise
61
-
62
- def analyze_with_gemini(self, video_path: str, video_filename: str) -> Optional[Dict]:
63
- """
64
- Analyze video using Gemini AI by uploading the video file directly
65
-
66
- Args:
67
- video_path: Path to video file
68
- video_filename: Name of video file for logging
69
-
70
- Returns:
71
- Dictionary with analysis results or None on failure
72
- """
73
- try:
74
- logger.info(f"Uploading video to Gemini: {video_filename}")
75
-
76
- # Upload video file to Gemini
77
- video_file = genai.upload_file(path=video_path)
78
-
79
- # Wait for video to be processed
80
- import time
81
- while video_file.state.name == "PROCESSING":
82
- time.sleep(1)
83
- video_file = genai.get_file(video_file.name)
84
-
85
- if video_file.state.name == "FAILED":
86
- logger.error(f"Video processing failed for {video_filename}")
87
- return None
88
-
89
- logger.info(f"Video uploaded successfully: {video_filename}")
90
-
91
- # Prepare content for Gemini (prompt + video)
92
- content = [self.prompt_template, video_file]
93
-
94
- # Call Gemini API
95
- response = self.model.generate_content(content)
96
-
97
- # Clean up uploaded file
98
- genai.delete_file(video_file.name)
99
-
100
- # Parse JSON response
101
- analysis = self.parse_gemini_response(response.text)
102
-
103
- if analysis:
104
- logger.info(f"AI analysis completed for {video_filename}")
105
- return analysis
106
- else:
107
- logger.error(f"Failed to parse Gemini response for {video_filename}")
108
- return None
109
-
110
- except Exception as e:
111
- logger.error(f"Error during Gemini analysis for {video_filename}: {e}")
112
- return None
113
-
114
- def parse_gemini_response(self, response_text: str) -> Optional[Dict]:
115
- """
116
- Parse and validate Gemini JSON response
117
-
118
- Args:
119
- response_text: Raw text response from Gemini
120
-
121
- Returns:
122
- Parsed dictionary or None if invalid
123
- """
124
- try:
125
- # Remove markdown code blocks if present
126
- text = response_text.strip()
127
- if text.startswith('```json'):
128
- text = text[7:]
129
- if text.startswith('```'):
130
- text = text[3:]
131
- if text.endswith('```'):
132
- text = text[:-3]
133
- text = text.strip()
134
-
135
- # Parse JSON
136
- data = json.loads(text)
137
-
138
- # Validate required fields (updated for new prompt format)
139
- required_fields = [
140
- 'category', 'short_description', 'description', 'luxury_score', 'visual_clarity_score',
141
- 'motion_score', 'beat_editing_score', 'recommended_edit_type',
142
- 'ideal_clip_length_seconds', 'energy_level',
143
- 'has_text_or_watermark', 'usable_for_ads', 'final_selection_score'
144
- ]
145
-
146
- for field in required_fields:
147
- if field not in data:
148
- logger.error(f"Missing required field: {field}")
149
- logger.debug(f"Received data: {json.dumps(data, indent=2)}")
150
- return None
151
-
152
- # Validate score ranges (all scores 0-100)
153
- score_fields = [
154
- 'luxury_score', 'visual_clarity_score', 'motion_score',
155
- 'beat_editing_score', 'final_selection_score'
156
- ]
157
- for score_field in score_fields:
158
- if not (0 <= data[score_field] <= 100):
159
- logger.error(f"Invalid score for {score_field}: {data[score_field]}")
160
- return None
161
-
162
- # Add has_faces_visible if Gemini didn't provide it (optional field)
163
- if 'has_faces_visible' not in data:
164
- data['has_faces_visible'] = False # Default to false
165
-
166
-
167
- # Validate enums
168
- valid_edit_types = ['static_hold', 'slow_pan', 'beat_cut', 'pulse_cut', 'transition_clip']
169
- if data['recommended_edit_type'] not in valid_edit_types:
170
- logger.error(f"Invalid edit type: {data['recommended_edit_type']}")
171
- return None
172
-
173
- valid_energy_levels = ['low', 'medium', 'high']
174
- if data['energy_level'] not in valid_energy_levels:
175
- logger.error(f"Invalid energy level: {data['energy_level']}")
176
- return None
177
-
178
- return data
179
-
180
- except json.JSONDecodeError as e:
181
- logger.error(f"Failed to parse JSON: {e}")
182
- logger.debug(f"Response text: {response_text}")
183
- return None
184
- except Exception as e:
185
- logger.error(f"Error parsing Gemini response: {e}")
186
- return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
video_analyser/modules/csv_writer.py DELETED
@@ -1,183 +0,0 @@
1
- """
2
- CSV Writer Module
3
- Write video analysis results to CSV in specified format
4
- """
5
-
6
- import csv
7
- import os
8
- import logging
9
- from typing import Dict
10
- from pathlib import Path
11
-
12
- logger = logging.getLogger(__name__)
13
-
14
-
15
- class CSVWriter:
16
- """Handle CSV output with exact column ordering"""
17
-
18
- # Exact column order as specified
19
- COLUMNS = [
20
- 'VIDEO_FILENAME',
21
- 'category',
22
- 'duration_seconds',
23
- 'short_description',
24
- 'description',
25
- 'luxury_score',
26
- 'visual_clarity_score',
27
- 'motion_score',
28
- 'beat_editing_score',
29
- 'recommended_edit_type',
30
- 'ideal_clip_length_seconds',
31
- 'energy_level',
32
- 'has_text_or_watermark',
33
- 'has_faces_visible',
34
- 'usable_for_ads',
35
- 'final_selection_score'
36
- ]
37
-
38
- def __init__(self, config: dict):
39
- """
40
- Initialize CSV Writer
41
-
42
- Args:
43
- config: Configuration dictionary with output settings
44
- """
45
- self.csv_file = config['output']['csv_file']
46
- self.initialized = False
47
-
48
- def initialize(self):
49
- """Initialize CSV file with headers if it doesn't exist"""
50
- if not os.path.exists(self.csv_file):
51
- with open(self.csv_file, 'w', newline='', encoding='utf-8') as f:
52
- writer = csv.DictWriter(f, fieldnames=self.COLUMNS)
53
- writer.writeheader()
54
- logger.info(f"Created CSV file: {self.csv_file}")
55
- else:
56
- logger.info(f"Using existing CSV file: {self.csv_file}")
57
-
58
- self.initialized = True
59
-
60
- def extract_category(self, video_path: str, local_dir: str) -> str:
61
- """
62
- Extract category from folder structure
63
-
64
- Args:
65
- video_path: Full path to video file
66
- local_dir: Base local directory
67
-
68
- Returns:
69
- Category name (folder name)
70
- """
71
- try:
72
- # Get relative path
73
- rel_path = os.path.relpath(video_path, local_dir)
74
-
75
- # Get parent folder name
76
- parts = Path(rel_path).parts
77
-
78
- if len(parts) > 1:
79
- # Category is the first folder
80
- return parts[0]
81
- else:
82
- return "uncategorized"
83
-
84
- except Exception as e:
85
- logger.error(f"Error extracting category from {video_path}: {e}")
86
- return "uncategorized"
87
-
88
- def validate_row(self, row: Dict) -> bool:
89
- """
90
- Validate row data before writing
91
-
92
- Args:
93
- row: Dictionary with row data
94
-
95
- Returns:
96
- True if valid, False otherwise
97
- """
98
- # Check all required columns present
99
- for col in self.COLUMNS:
100
- if col not in row:
101
- logger.error(f"Missing column: {col}")
102
- return False
103
-
104
- # Validate data types
105
- try:
106
- # Numeric fields
107
- assert isinstance(row['duration_seconds'], (int, float))
108
- assert isinstance(row['luxury_score'], int)
109
- assert isinstance(row['visual_clarity_score'], int)
110
- assert isinstance(row['motion_score'], int)
111
- assert isinstance(row['beat_editing_score'], int)
112
- assert isinstance(row['ideal_clip_length_seconds'], (int, float))
113
- assert isinstance(row['final_selection_score'], int)
114
-
115
- # String fields
116
- assert isinstance(row['VIDEO_FILENAME'], str)
117
- assert isinstance(row['category'], str)
118
- assert isinstance(row['short_description'], str)
119
- assert isinstance(row['description'], str)
120
- assert isinstance(row['recommended_edit_type'], str)
121
- assert isinstance(row['energy_level'], str)
122
-
123
- # Boolean fields (should be strings 'true' or 'false')
124
- assert row['has_text_or_watermark'] in ['true', 'false']
125
- assert row['has_faces_visible'] in ['true', 'false']
126
- assert row['usable_for_ads'] in ['true', 'false']
127
-
128
- return True
129
-
130
- except AssertionError as e:
131
- logger.error(f"Row validation failed: {e}")
132
- return False
133
-
134
- def write_row(self, video_path: str, technical_analysis: Dict,
135
- ai_analysis: Dict, scoring_results: Dict, local_dir: str):
136
- """
137
- Write a single row to CSV
138
-
139
- Args:
140
- video_path: Path to video file
141
- technical_analysis: Results from VideoAnalyzer
142
- ai_analysis: Results from AIAnalyzer
143
- scoring_results: Results from Scorer
144
- local_dir: Base local directory for extracting category
145
- """
146
- if not self.initialized:
147
- self.initialize()
148
-
149
- # Prepare row data
150
- row = {
151
- 'VIDEO_FILENAME': os.path.basename(video_path),
152
- 'category': ai_analysis.get('category', 'uncategorized'),
153
- 'duration_seconds': technical_analysis['duration_seconds'],
154
- 'short_description': ai_analysis.get('short_description', ''),
155
- 'description': ai_analysis['description'],
156
- 'luxury_score': scoring_results['luxury_score'],
157
- 'visual_clarity_score': scoring_results['visual_clarity_score'],
158
- 'motion_score': scoring_results['motion_score'],
159
- 'beat_editing_score': scoring_results['beat_editing_score'],
160
- 'recommended_edit_type': ai_analysis['recommended_edit_type'],
161
- 'ideal_clip_length_seconds': scoring_results['ideal_clip_length_seconds'],
162
- 'energy_level': ai_analysis['energy_level'],
163
- 'has_text_or_watermark': 'true' if ai_analysis['has_text_or_watermark'] else 'false',
164
- 'has_faces_visible': 'true' if ai_analysis.get('has_faces_visible', False) else 'false',
165
- 'usable_for_ads': 'true' if ai_analysis['usable_for_ads'] else 'false',
166
- 'final_selection_score': scoring_results['final_selection_score']
167
- }
168
-
169
- # Validate before writing
170
- if not self.validate_row(row):
171
- logger.error(f"Skipping invalid row for {video_path}")
172
- return
173
-
174
- # Write to CSV
175
- try:
176
- with open(self.csv_file, 'a', newline='', encoding='utf-8') as f:
177
- writer = csv.DictWriter(f, fieldnames=self.COLUMNS)
178
- writer.writerow(row)
179
-
180
- logger.debug(f"Wrote row to CSV for {os.path.basename(video_path)}")
181
-
182
- except Exception as e:
183
- logger.error(f"Error writing row to CSV: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
video_analyser/modules/drive_downloader.py DELETED
@@ -1,245 +0,0 @@
1
- """
2
- Google Drive Downloader Module
3
- Downloads videos from Google Drive maintaining folder structure
4
- """
5
-
6
- import os
7
- import io
8
- import logging
9
- from pathlib import Path
10
- from typing import List, Dict, Optional
11
- from tqdm import tqdm
12
-
13
- from google.auth.transport.requests import Request
14
- from google.oauth2.credentials import Credentials
15
- from googleapiclient.discovery import build
16
- from googleapiclient.http import MediaIoBaseDownload
17
-
18
- logger = logging.getLogger(__name__)
19
-
20
- # Note: drive.file only allows access to files created by the app
21
- # For accessing existing shared folders, we need broader scope
22
- SCOPES = [
23
- 'https://www.googleapis.com/auth/drive.readonly'
24
- ]
25
-
26
-
27
- class DriveDownloader:
28
- """Handle Google Drive authentication and video downloads"""
29
-
30
- def __init__(self, config: dict):
31
- """
32
- Initialize Drive Downloader
33
-
34
- Args:
35
- config: Configuration dictionary with google_drive settings
36
- """
37
- self.folder_id = config['google_drive']['folder_id']
38
- self.local_dir = config['output']['local_video_dir']
39
- self.service = None
40
-
41
- def authenticate(self):
42
- """Authenticate with Google Drive API using environment variables"""
43
- # Get OAuth credentials from environment
44
- client_id = os.getenv('SERVER_GOOGLE_CLIENT_ID')
45
- client_secret = os.getenv('SERVER_GOOGLE_CLIENT_SECRET')
46
-
47
- # Try Drive-specific token first, fall back to server token
48
- refresh_token = os.getenv('SERVER_GOOGLE_REFRESH_TOKEN')
49
-
50
- if not all([client_id, client_secret, refresh_token]):
51
- raise ValueError(
52
- "Missing Google OAuth credentials. Required environment variables:\n"
53
- " - SERVER_GOOGLE_CLIENT_ID\n"
54
- " - SERVER_GOOGLE_CLIENT_SECRET\n"
55
- "Run 'python video_analyser/get_refresh_token.py' to get a Drive token."
56
- )
57
-
58
- # Create credentials from environment variables
59
- creds = Credentials(
60
- token=None,
61
- refresh_token=refresh_token,
62
- token_uri='https://oauth2.googleapis.com/token',
63
- client_id=client_id,
64
- client_secret=client_secret,
65
- scopes=SCOPES
66
- )
67
-
68
- # Refresh the token
69
- creds.refresh(Request())
70
-
71
- self.service = build('drive', 'v3', credentials=creds)
72
- logger.info("Successfully authenticated with Google Drive")
73
-
74
- def list_folder_structure(self, folder_id: Optional[str] = None) -> List[Dict]:
75
- """
76
- List all files and folders recursively
77
-
78
- Args:
79
- folder_id: Folder ID to list (defaults to root folder)
80
-
81
- Returns:
82
- List of file/folder metadata dictionaries
83
- """
84
- if folder_id is None:
85
- folder_id = self.folder_id
86
-
87
- items = []
88
-
89
- # Query for all items in folder
90
- query = f"'{folder_id}' in parents and trashed=false"
91
- results = self.service.files().list(
92
- q=query,
93
- fields="files(id, name, mimeType, size)",
94
- pageSize=1000
95
- ).execute()
96
-
97
- files = results.get('files', [])
98
-
99
- for file in files:
100
- if file['mimeType'] == 'application/vnd.google-apps.folder':
101
- # Recursively get folder contents
102
- subfolder_items = self.list_folder_structure(file['id'])
103
- items.extend([
104
- {**item, 'path': f"{file['name']}/{item['path']}"}
105
- for item in subfolder_items
106
- ])
107
- else:
108
- # Add file with relative path
109
- items.append({
110
- 'id': file['id'],
111
- 'name': file['name'],
112
- 'path': file['name'],
113
- 'size': int(file.get('size', 0)),
114
- 'mimeType': file['mimeType']
115
- })
116
-
117
- return items
118
-
119
- def download_file(self, file_id: str, destination: str) -> bool:
120
- """
121
- Download a single file from Google Drive
122
-
123
- Args:
124
- file_id: Google Drive file ID
125
- destination: Local file path to save to
126
-
127
- Returns:
128
- True if successful, False otherwise
129
- """
130
- try:
131
- # Create parent directory if needed
132
- os.makedirs(os.path.dirname(destination), exist_ok=True)
133
-
134
- # Check if file already exists
135
- if os.path.exists(destination):
136
- logger.debug(f"Skipping existing file: {destination}")
137
- return True
138
-
139
- # Download file
140
- request = self.service.files().get_media(fileId=file_id)
141
- fh = io.BytesIO()
142
-
143
- downloader = MediaIoBaseDownload(fh, request)
144
- done = False
145
-
146
- while not done:
147
- status, done = downloader.next_chunk()
148
-
149
- # Write to file
150
- with open(destination, 'wb') as f:
151
- fh.seek(0)
152
- f.write(fh.read())
153
-
154
- logger.debug(f"Downloaded: {destination}")
155
- return True
156
-
157
- except Exception as e:
158
- logger.error(f"Failed to download {destination}: {e}")
159
- return False
160
-
161
- def download_all(self) -> List[str]:
162
- """
163
- Download all videos from Drive maintaining folder structure
164
-
165
- Returns:
166
- List of local file paths that were downloaded
167
- """
168
- if not self.service:
169
- self.authenticate()
170
-
171
- logger.info(f"Listing files in Google Drive folder...")
172
- items = self.list_folder_structure()
173
-
174
- # Filter for video files only
175
- video_extensions = ['.mp4', '.mov', '.avi', '.mkv', '.webm']
176
- video_files = [
177
- item for item in items
178
- if any(item['name'].lower().endswith(ext) for ext in video_extensions)
179
- ]
180
-
181
- logger.info(f"Found {len(video_files)} video files to download")
182
-
183
- downloaded_files = []
184
-
185
- # Download with progress bar
186
- with tqdm(total=len(video_files), desc="Downloading videos") as pbar:
187
- for item in video_files:
188
- local_path = os.path.join(self.local_dir, item['path'])
189
-
190
- if self.download_file(item['id'], local_path):
191
- downloaded_files.append(local_path)
192
-
193
- pbar.update(1)
194
-
195
- logger.info(f"Successfully downloaded {len(downloaded_files)} videos")
196
- return downloaded_files
197
-
198
- def list_all_videos(self) -> List[Dict]:
199
- """
200
- List all videos from Google Drive without downloading
201
-
202
- Returns:
203
- List of video items with metadata (id, name, path)
204
- """
205
- if not self.service:
206
- self.authenticate()
207
-
208
- logger.info("Listing files in Google Drive folder...")
209
- items = self.list_folder_structure()
210
-
211
- # Filter for video files only
212
- video_extensions = ['.mp4', '.mov', '.avi', '.mkv', '.webm']
213
- video_files = [
214
- item for item in items
215
- if any(item['name'].lower().endswith(ext) for ext in video_extensions)
216
- ]
217
-
218
- logger.info(f"Found {len(video_files)} video files")
219
- return video_files
220
-
221
- def download_single_video(self, item: Dict) -> Optional[str]:
222
- """
223
- Download a single video from Google Drive
224
-
225
- Args:
226
- item: Video item dict with 'id', 'name', 'path'
227
-
228
- Returns:
229
- Local path to downloaded file or None on failure
230
- """
231
- local_path = os.path.join(self.local_dir, item['path'])
232
-
233
- # Skip if already exists
234
- if os.path.exists(local_path):
235
- logger.debug(f"Video already exists locally: {item['name']}")
236
- return local_path
237
-
238
- # Create directory
239
- os.makedirs(os.path.dirname(local_path), exist_ok=True)
240
-
241
- # Download file
242
- if self.download_file(item['id'], local_path):
243
- return local_path
244
- else:
245
- return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
video_analyser/modules/git_ops.py DELETED
@@ -1,121 +0,0 @@
1
- """
2
- Git operations for progress tracking
3
- """
4
-
5
- import os
6
- import subprocess
7
- import logging
8
- import time
9
- import random
10
- from pathlib import Path
11
-
12
- logger = logging.getLogger(__name__)
13
-
14
-
15
- def git_commit_progress(
16
- csv_file: str,
17
- log_file: str,
18
- progress_file: Path,
19
- job_index: int = None,
20
- commit: bool = False
21
- ):
22
- """
23
- Commit progress for a specific job with retry logic for parallel environments.
24
-
25
- Args:
26
- csv_file: Path to CSV file to commit
27
- log_file: Path to log file to commit
28
- progress_file: Path to progress tracking file
29
- job_index: Job index for parallel processing
30
- commit: Whether to actually commit (dry-run if False)
31
- """
32
- if not commit:
33
- logger.info("ℹ️ Skipping git commit (use --commit flag to enable).")
34
- return
35
-
36
- if not progress_file.exists():
37
- logger.info(f"ℹ️ No progress file found. Nothing to commit.")
38
- return
39
-
40
- try:
41
- # Get branch from environment or default to feature/video-revamp
42
- branch = os.getenv('GIT_BRANCH', 'feature/video-revamp')
43
- max_retries = 3
44
-
45
- job_label = f"job {job_index}" if job_index is not None else "main"
46
-
47
- # 1. Ensure we're on the correct branch
48
- logger.info(f"Git: Ensuring we are on branch '{branch}' for {job_label}...")
49
- subprocess.run(["git", "fetch", "origin", branch], check=True, capture_output=True)
50
- subprocess.run(
51
- ["git", "checkout", "-B", branch, f"origin/{branch}"],
52
- check=True,
53
- capture_output=True
54
- )
55
-
56
- # 2. Stage and commit local changes (force add to include log files)
57
- files_to_commit = [csv_file, str(progress_file)]
58
- subprocess.run(["git", "add"] + files_to_commit, check=True)
59
-
60
- commit_msg = f"🎬 Video analysis progress - {job_label}" if job_index is not None else "🎬 Video analysis progress"
61
-
62
- commit_result = subprocess.run(
63
- ["git", "commit", "-m", commit_msg],
64
- capture_output=True,
65
- text=True
66
- )
67
-
68
- # Check if commit was successful or if nothing to commit
69
- if commit_result.returncode != 0:
70
- if "nothing to commit" in commit_result.stdout or "nothing to commit" in commit_result.stderr:
71
- logger.info(f"ℹ️ No new progress to commit for {job_label}.")
72
- return
73
- else:
74
- raise subprocess.CalledProcessError(
75
- commit_result.returncode,
76
- cmd=commit_result.args,
77
- output=commit_result.stdout,
78
- stderr=commit_result.stderr
79
- )
80
-
81
- # 3. Push with retry loop for race conditions
82
- for attempt in range(max_retries):
83
- try:
84
- # Pull with rebase before push
85
- logger.info(f"Git: Pulling with rebase (Attempt {attempt + 1}/{max_retries})...")
86
- subprocess.run(
87
- ["git", "pull", "--rebase", "origin", branch],
88
- check=True,
89
- capture_output=True
90
- )
91
-
92
- # Attempt push
93
- logger.info(f"Git: Pushing to remote (Attempt {attempt + 1}/{max_retries})...")
94
- subprocess.run(
95
- ["git", "push", "origin", branch],
96
- check=True,
97
- timeout=45
98
- )
99
-
100
- logger.info(f"✓ Committed progress successfully for {job_label}")
101
- return
102
-
103
- except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as e:
104
- if attempt < max_retries - 1:
105
- # Randomized exponential backoff
106
- sleep_duration = random.uniform(2, 5) * (attempt + 1)
107
- logger.warning(
108
- f"Push failed for {job_label}. Retrying in {sleep_duration:.2f} seconds..."
109
- )
110
- time.sleep(sleep_duration)
111
- else:
112
- logger.error(
113
- f"❌ Failed to push progress for {job_label} after {max_retries} attempts."
114
- )
115
- raise
116
-
117
- except subprocess.CalledProcessError as e:
118
- error_message = e.stderr if e.stderr else e.stdout
119
- logger.error(f"❌ Git command failed for {job_label}: {e.cmd}\nError: {error_message}")
120
- subprocess.run(["git", "reset", "--hard"])
121
- raise
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
video_analyser/modules/scorer.py DELETED
@@ -1,199 +0,0 @@
1
- """
2
- Scorer Module
3
- Calculate final selection scores using weighted formulas
4
- """
5
-
6
- import logging
7
- from typing import Dict
8
-
9
- logger = logging.getLogger(__name__)
10
-
11
-
12
- class Scorer:
13
- """Calculate weighted scores for video selection"""
14
-
15
- def __init__(self, config: dict):
16
- """
17
- Initialize Scorer
18
-
19
- Args:
20
- config: Configuration dictionary with scoring weights
21
- """
22
- self.luxury_weight = config['scoring']['luxury_weight']
23
- self.visual_clarity_weight = config['scoring']['visual_clarity_weight']
24
- self.beat_editing_weight = config['scoring']['beat_editing_weight']
25
- self.motion_weight = config['scoring']['motion_weight']
26
- self.usability_weight = config['scoring']['usability_weight']
27
-
28
- # Validate weights sum to 1.0
29
- total = sum([
30
- self.luxury_weight,
31
- self.visual_clarity_weight,
32
- self.beat_editing_weight,
33
- self.motion_weight,
34
- self.usability_weight
35
- ])
36
-
37
- if abs(total - 1.0) > 0.01:
38
- logger.warning(f"Scoring weights sum to {total}, not 1.0. Normalizing...")
39
- norm_factor = 1.0 / total
40
- self.luxury_weight *= norm_factor
41
- self.visual_clarity_weight *= norm_factor
42
- self.beat_editing_weight *= norm_factor
43
- self.motion_weight *= norm_factor
44
- self.usability_weight *= norm_factor
45
-
46
- def validate_scores(self, scores: Dict) -> bool:
47
- """
48
- Validate that all scores are within 0-100 range
49
-
50
- Args:
51
- scores: Dictionary with score values
52
-
53
- Returns:
54
- True if all scores valid, False otherwise
55
- """
56
- score_fields = [
57
- 'luxury_score',
58
- 'visual_clarity_score',
59
- 'motion_score',
60
- 'beat_editing_score'
61
- ]
62
-
63
- for field in score_fields:
64
- if field not in scores:
65
- logger.error(f"Missing score field: {field}")
66
- return False
67
-
68
- if not (0 <= scores[field] <= 100):
69
- logger.error(f"Invalid score for {field}: {scores[field]}")
70
- return False
71
-
72
- return True
73
-
74
- def calculate_usability_bonus(self, has_text: bool, has_faces: bool,
75
- usable_for_ads: bool) -> int:
76
- """
77
- Calculate usability bonus score
78
-
79
- Args:
80
- has_text: Whether video has text/watermarks
81
- has_faces: Whether video has visible faces
82
- usable_for_ads: Whether video is commercially safe
83
-
84
- Returns:
85
- Usability bonus score (0-100)
86
- """
87
- score = 100
88
-
89
- # Penalize text/watermarks
90
- if has_text:
91
- score -= 40
92
-
93
- # Slight penalty for faces (can limit usage scenarios)
94
- if has_faces:
95
- score -= 20
96
-
97
- # Bonus for ad-safe content
98
- if not usable_for_ads:
99
- score -= 30
100
-
101
- return max(0, min(100, score))
102
-
103
- def determine_ideal_clip_length(self, motion_score: int, energy_level: str,
104
- duration: float) -> float:
105
- """
106
- Determine ideal clip length based on motion and energy
107
-
108
- Args:
109
- motion_score: Motion score (0-100)
110
- energy_level: Energy level (low/medium/high)
111
- duration: Total video duration
112
-
113
- Returns:
114
- Ideal clip length in seconds
115
- """
116
- # Base lengths by energy level
117
- base_lengths = {
118
- 'low': 2.0,
119
- 'medium': 1.5,
120
- 'high': 1.0
121
- }
122
-
123
- base = base_lengths.get(energy_level, 1.5)
124
-
125
- # Adjust based on motion
126
- if motion_score > 70:
127
- # High motion = shorter clips work better
128
- base *= 0.8
129
- elif motion_score < 30:
130
- # Low motion = can hold longer
131
- base *= 1.3
132
-
133
- # Cap at total duration
134
- return min(base, duration)
135
-
136
- def calculate_final_score(self, technical_analysis: Dict,
137
- ai_analysis: Dict) -> Dict:
138
- """
139
- Calculate final selection score using weighted formula
140
-
141
- Args:
142
- technical_analysis: Results from VideoAnalyzer (duration only)
143
- ai_analysis: Results from AIAnalyzer (all scores from Gemini)
144
-
145
- Returns:
146
- Dictionary with final score and all component scores
147
- """
148
- # Extract scores from AI analysis (Gemini provides everything)
149
- luxury_score = ai_analysis['luxury_score']
150
- visual_clarity_score = ai_analysis['visual_clarity_score']
151
- beat_editing_score = ai_analysis['beat_editing_score']
152
- motion_score = ai_analysis['motion_score']
153
-
154
- # Calculate usability bonus
155
- usability_score = self.calculate_usability_bonus(
156
- ai_analysis['has_text_or_watermark'],
157
- ai_analysis.get('has_faces_visible', False),
158
- ai_analysis['usable_for_ads']
159
- )
160
-
161
- # Validate scores
162
- all_scores = {
163
- 'luxury_score': luxury_score,
164
- 'visual_clarity_score': visual_clarity_score,
165
- 'motion_score': motion_score,
166
- 'beat_editing_score': beat_editing_score
167
- }
168
-
169
- if not self.validate_scores(all_scores):
170
- logger.error("Score validation failed")
171
- return None
172
-
173
- # Calculate weighted final score
174
- final_score = (
175
- luxury_score * self.luxury_weight +
176
- visual_clarity_score * self.visual_clarity_weight +
177
- beat_editing_score * self.beat_editing_weight +
178
- motion_score * self.motion_weight +
179
- usability_score * self.usability_weight
180
- )
181
-
182
- # Round to integer
183
- final_score = int(round(final_score))
184
-
185
- # Determine ideal clip length
186
- ideal_clip_length = self.determine_ideal_clip_length(
187
- motion_score,
188
- ai_analysis['energy_level'],
189
- technical_analysis['duration_seconds']
190
- )
191
-
192
- return {
193
- 'luxury_score': luxury_score,
194
- 'visual_clarity_score': visual_clarity_score,
195
- 'motion_score': motion_score,
196
- 'beat_editing_score': beat_editing_score,
197
- 'final_selection_score': final_score,
198
- 'ideal_clip_length_seconds': round(ideal_clip_length, 1)
199
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
video_analyser/modules/video_analyzer.py DELETED
@@ -1,66 +0,0 @@
1
- """
2
- Video Analyzer Module
3
- Extracts basic technical metadata from video files
4
- """
5
-
6
- import cv2
7
- import logging
8
- from typing import Dict
9
-
10
- logger = logging.getLogger(__name__)
11
-
12
-
13
- class VideoAnalyzer:
14
- """Extract basic technical metadata from video files"""
15
-
16
- def __init__(self, config: dict):
17
- """
18
- Initialize Video Analyzer
19
-
20
- Args:
21
- config: Configuration dictionary
22
- """
23
- pass # No config needed for simple duration extraction
24
-
25
- def get_video_duration(self, video_path: str) -> float:
26
- """
27
- Get video duration in seconds
28
-
29
- Args:
30
- video_path: Path to video file
31
-
32
- Returns:
33
- Duration in seconds
34
- """
35
- try:
36
- cap = cv2.VideoCapture(video_path)
37
- fps = cap.get(cv2.CAP_PROP_FPS)
38
- frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
39
- cap.release()
40
-
41
- if fps > 0:
42
- duration = frame_count / fps
43
- return round(duration, 2)
44
- else:
45
- logger.warning(f"Could not determine FPS for {video_path}")
46
- return 0.0
47
-
48
- except Exception as e:
49
- logger.error(f"Error getting duration for {video_path}: {e}")
50
- return 0.0
51
-
52
- def analyze_video(self, video_path: str) -> Dict:
53
- """
54
- Perform basic technical analysis of video
55
-
56
- Args:
57
- video_path: Path to video file
58
-
59
- Returns:
60
- Dictionary with basic analysis results
61
- """
62
- logger.info(f"Analyzing video: {video_path}")
63
-
64
- return {
65
- 'duration_seconds': self.get_video_duration(video_path)
66
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
video_analyser/progress/analyzed_videos.txt DELETED
@@ -1,1028 +0,0 @@
1
- Copy of SnapInsta.to_AQM-w-YmCepAaXIWl7Frs10cKbdX1P-__zSTTzh7j0td9hh6ebAJWA409L_fJNcgiWctukdKl0ubkT2tNOxgjyRdOXs3nPBgMAgWV90.mp4
2
- Copy of SnapInsta.to_AQM2gxxnIBP-Ah5vZhdj-LKAZSNJYynCBrXUc7PmC5N43Nhcambj7NOYu0uN9GD8JtEe6V7QdHaGjxLB2H26XycERUR536H_CG4o8NI.mp4
3
- Copy of SnapInsta.to_AQMKNeqwx4_JXcTg8QX5WQMDD1UJcuxqnKgiPDRi0UYoKjJkppR5AFMmLiQ66ZiLXWi5Db8Kk9chsM6ZShLMgtt5EdoSEShH7aPnfS8.mp4
4
- Copy of SnapInsta.to_AQMMtHxghW_GerwQOpnEAYGYkAGqj5kQ7bddgin5kvyHny4Ogn0vuANSJemeNdwntJbLE60E3bP6V01c4G4h5CgqDbuFqvlmb1yfqKY.mp4
5
- Copy of SnapInsta.to_AQMnfLf26HWnswIIriCayFTt9LyRvdv6CuIpnlzBNZkjbEAwhp-31XiHZFL15ybWSq0jyGfW8OlC2XRDXxOrM2sivaFd7esAKuZRpmk.mp4
6
- Copy of SnapInsta.to_AQMJ3dwasTBsR6jCGwIhuDu4-uC1K5x7tVNwtF5DUj_uLlCnYAMC-H45aFDgCsYf-0Wjj2cvCMza_SYh1W8vgB155t-X7oLq--Rxexc.mp4
7
- Copy of SnapInsta.to_AQMhX9lMqY0zpKz4doF8I_MQaDDBKVrNtEshVWGuvOQeb-lW8oMK_L7mlGt3OqqfuzQmECl_nO9Cw-wO6f9mkEV2NHbW7RoxgJA8BoI.mp4
8
- Copy of SnapInsta.to_AQMJB1N4IAxMjpDfH5LNe3m14w9dYoNdsquK-3IraJoEG4km46WyNVwOEggscyv9yiOcl-9CvHryJyKvuz2e4UflgI8cfCeKImkTnIs.mp4
9
- Copy of SnapInsta.to_AQMhpxUr31qzAnyFgG-CGcTZA7YauGZttnT3E3QmvdMQzIpwdaXfRNp_t4rglku1ssx0YWQTC5qS3XfrkKj891YIyJwwoMb3zjYSSE8.mp4
10
- Copy of SnapInsta.to_AQMjGwqhUGTz_vUednkqlU72EgCDpeWMkoRq6V4hS2kfadbVPZeAcVHf8asV_-8urVDnd-kU4diRtYLs2L9xMru3xQKSaNXDyT9Vyr4.mp4
11
- Copy of SnapInsta.to_AQMMblIb4Q9fqGa0PFRpMgDde6VC-WSvBnA5-VC_FWN8_E3GuSE9UoItdYVX4CPDHx6J9ux7Hb_0gSlIvqJc8nFNvXXXDed2s19RHdg.mp4
12
- Copy of SnapInsta.to_AQMVbKl_NzLEztr9dmjtZTZ0lKUpr5LZZ-AnOEzDaoyfotSFh0rcLtB5_BylffGQ2xMmirPcwILSFVTmm_qR3Z6pRSsyNZVJeGUr7TU.mp4
13
- Copy of SnapInsta.to_AQMp8l7iAUBT36_-iZYlXVUr2TmW5Y3q1cqXQ0HRtzJ8hLxpsiw4YfTZIGkOoGmULYZot81ISTgk6ctGHlW7VrmrcN9Ilg25XA-tm38.mp4
14
- Copy of SnapInsta.to_AQMRA4xtD_OFQNjmoD4Ijj3xmCTUzDdZ_CIUMJm27i-DzkROgowPSwN3tEUn7U6tjrfqfDDgJf6g4A9cMqvbnmv6urUV53WKP5ERpLk.mp4
15
- Copy of SnapInsta.to_AQMtt7cBbT4sG5cwVbdrOJ1eVXZNC3BMbTDHl2Wqtb9SBLYW7lUcchJDmTUFb_IUd00QjMfsvz01RG6VHuwgUvd4BOrdOXkIEZARtX8.mp4
16
- Copy of SnapInsta.to_AQMNqE9oT2DvjsdWIKYSfRg03MZqIFPC1c-ips0s-EjsL5GU1InZtra9S6u6FUBEaMey764evwvevEGSOXhYGucCwvSXtGCAC1FWKOU.mp4
17
- Copy of SnapInsta.to_AQMnRldXqsRBmhumUES2uyOjiVhKCfAz_DBJe1-IA7cf8Fjg7rQo75iP3QEKdm9IwQa1HLKIpwkUZKYGYua2C9dyxi2f2EFHmV-YQX8.mp4
18
- Copy of SnapInsta.to_AQMUlVUdL9-pHqUjzex1uIMIJHBU2uTO588nHN-pAn02UXMxMB0AZ8gRLa3mN6pNArOEl9Di6Jte542K-8SyePfPoKtm6f8ZTd-Z_d8.mp4
19
- Copy of SnapInsta.to_AQMQqfrRHbsMNGEdgk59VWjKiiQMcob0JKxpZplExBCp6-MzSO6RhrhAOJaC9rDT9wR9kCDccxAUxgNxwo7icC9aMGu2j5XekOlRZUo.mp4
20
- Copy of SnapInsta.to_AQMqtr6waTHNbOU_nRabAaNlR4yLSYKVZ9cNRwuEAC_zijlbOrIjiHQWaRy5u9_TDp2Xq2TcMR7XJbsiX0UZId4u.mp4
21
- Copy of SnapInsta.to_AQMSR2D0e05gi9FVMTe_7XSXb0fE_IKW-MVaUMtdG5-SM8juQuGDtJJNg-k0Foc-69Ha-hPtH_clherA_rksqYI3vemtnbpJyyEaVcU.mp4
22
- Copy of SnapInsta.to_AQMVonW8Vl8BUXM5WSHgI24yYXI7cyvbmPozlZTML5rchIhpM_zn1DkWh-ow47hNd_2qR8RFAnRjOa8o3tnwTgSz2plWQMtNCrz7ar0.mp4
23
- Copy of SnapInsta.to_AQN-ZmWaWlIe7yQiZukzWBSxwYahU42ceLu6kge4wOmXjGX93ifAXSV3qCDq9twQkJLpWTMqI5-udhYz8NJBdoV3kz0nZhPbnYqbQac.mp4
24
- Copy of SnapInsta.to_AQMXUx2H1BriR0m_uwuP3esxRa0m_Xnx5y0DcAoSCVM1hmoxke8tQ0oFgenLuFAyoTfzBePFN5tpJR4-n1MzQ1BMBTLbI8sPRuo5bAg.mp4
25
- Copy of SnapInsta.to_AQMwKLVO-lEZ3otDL9LlfAhBMlefBtAnZDcQN1S-TmGDpbgCCPW1sjLLXEIyeKRm_0JE6aKENDvmkW0PYxNcVFx_Oa5rGipeFH5Cpag.mp4
26
- Copy of SnapInsta.to_AQMVO_6qUs8_TzGX7WW_xp_2T5g92hqlJH1c7IufFqOcWAS-VwdinZPWG9ltQ_-Jw-pVx9Lp956n2Ag_jdS43omUWowIgYEFF3MKJEg.mp4
27
- Copy of SnapInsta.to_AQN-XiVMC5LInVoo8hCMEV31-Jl5oywpM1at9dN5rl3Udwt7GON1BW5cv2fPaTQlY78r591RcRo3YhKPwYtmwNuBxAdsaKi-nsNBK_s.mp4
28
- Copy of SnapInsta.to_AQN_KFeCp0oZzSjqZNxe3i82lgEm01TfUIomhp1MPp2nsDJiLCg8Cxb0TU2gBLdIf99YsH6Oy_CeO0Ui5w7vA925vHmWVb_npzusPac.mp4
29
- Copy of SnapInsta.to_AQMZbe_7iCqWDT-xsvq7fdyoch9XGYYzAN015Tl_04Fljs25mbu9fr1AmJ-ErtX9-3-vjSqCgcwzLKJ5b3WDZvZk9oNQSNjcayVZ7PY.mp4
30
- Copy of SnapInsta.to_AQMVnlnpH0ivITy367-Qzlvvu8_QkjcDnpQttkWChXZnLse1GA3dCBy6u_JwqQwgr7w5g8DdIaxukOdnVL8UuYer.mp4
31
- Copy of SnapInsta.to_AQMwigxgToUYIi5AUCIt1lV2QKpvADh_rG5wrwe-hUbyhDywBl3L4V1238063Fh50UcVnLo9Ht85pU88Umzi1ml4nJTJz8xbZK8-B04.mp4
32
- Copy of SnapInsta.to_AQNB23wNX8ndmbIl1obUu25fjr1HQ_fhVwbIO2LEpVXx3TRSk8UQoyusbR8wbLegbbwaAl0NMzoKuRCg1osDFkbLa6553aHKCyUhr3g.mp4
33
- Copy of SnapInsta.to_AQNbmIyZBO6TaHbzd-ihFi9pWpJ_Du_Bxi35hXj7vnZbyU46XWiNBwMEE14As08Xjk7rMNEC2g_PcwuaA3fT0KaBCHFvCTqFD6KXHcs.mp4
34
- Copy of SnapInsta.to_AQNaziUShlmtsJcJu1mxqhoETbCFd3L9GU82Md3eeBfyHWdNwiSTlR-cfG8vA9Jp86CBrc6vXSmEJVx0SOQJgkhLkoSWe3DjYP6NzD4.mp4
35
- Copy of SnapInsta.to_AQN2LexHmGhjLOmLQwvrXaixepIIjO4qgK7s_FOy6855oISLSqousqWDesQXNAyqDJW8VCnyd14kl6PUIQIQIeO1jqN8SNrgH4XL9Uk.mp4
36
- Copy of SnapInsta.to_AQN8TpDHAqjLs7s65r9zJ7CKbKiBFkp6PnWIdUAeYxh43GBIz8D1OnWjIEC71hO8NzLHi19xCkGvNu48AQOjdos4c-hrrA3orfhBZn4.mp4
37
- Copy of SnapInsta.to_AQN0QCqUS-Kdc2chNW-WAni2D5V3I5hgxb4e7sVeGe_w6zys7oriPnE0xhjFvkHuqWZ2Auo-FitL9HJZE6PSjFXJOOxgZR6mgILCkAw.mp4
38
- Copy of SnapInsta.to_AQN4KkjrOC6aqzf-ju-U7xu1XNST8vVIOq8lw3mtKaX-3u2WAqCBhVxN07m4UzyiSk-qqQRST8Wm2EiZioG6JhoYP6rNb7z_xkWiKpw.mp4
39
- Copy of SnapInsta.to_AQN0M7uhrwylRo8FdA8134TTuI-xL3R5pk6qC734VYpcw7wiQeIyXSl6HnvtnLSe6j4wS3IQ6_y1jhEFAm9K_ZDgxKL4MMGKYcqfXWI.mp4
40
- Copy of SnapInsta.to_AQNBorwAKypx1gZsrLnwkH223yqR4cFsSUcn2OnYGoDxSn-EWis0OHwQyl73azOVk7QWFQFRktGIPVIODtcijRRJKW23KECTAT3LK5Y.mp4
41
- Copy of SnapInsta.to_AQN9M60i5k1SFj_ozWuIWLon9Kgyw7e565ccbmrEqr_M0-N8HOVkuqAUCjyg_GKud4uy3E3ySPuTg048j2Wm6MZVH6yzPnn45Ebh5F8.mp4
42
- Copy of SnapInsta.to_AQNckYpG_uQBOOLUwHAUDWMuWhlg2trg-Y9Kp3DGr2S6h4MpAdmcQaKAJBfDqeujh7E2kDgKsmACsQY2GhnTYm5DiDwvs--huo1RW0k.mp4
43
- Copy of SnapInsta.to_AQNfGROswAa-Z9DDIBxZq3yu91rkXULf-jOaopJ0PqFogwhvHWDJBFmk_3irrnkef29SgmtpWI-mMJInXFA8kGpLI4CWZy00I7g0xWw.mp4
44
- Copy of SnapInsta.to_AQNG5Tnyx0q11YTJ1hW_VMRnj1Eh8TjZc5ID9RGqt6fNiUJ3-lsZK1mBb9ACL_vv7aCI7t4iTwT15oJXzY94CyeFBLXUJw1M48KNHs8.mp4
45
- Copy of SnapInsta.to_AQNM2sAMPV1bcw50rGWAgRhVsyC3HvhSAXfsb-Rlbl5K2IQ92hI0kyIvMtkytKMRTfWcZNzQzCy764eTJ8Z6fvZcCulMmwSGt8nLNVg.mp4
46
- Copy of SnapInsta.to_AQNJVbzuPU9fiNI1KZ6VfnH_zPO2pO2O_4Q6DgfWnej3iPOFY9MllBGw22m2V0F_rqjHBuiGhPhzM5DdCRkgQnKA8dICtgOL6X_ZCd4.mp4
47
- Copy of SnapInsta.to_AQNcGVi2gcFCQ4nCsAx6tcOQof_n9qlcQ4Tu9XqzBXPOaIYyT4cHGjQtrz50N-DDytf5Rmr-REWSAh0BPs2yBSLkhJqNbnSp_QmmWBk.mp4
48
- Copy of SnapInsta.to_AQNkIsZQNxiE7SPGe33VjuZp_ZlfHCKO0V5TzGUxzzKSZ5WzrO1GLoPmUzUspGtuTC-9XlmVsNI9C7UdLkBiqdwD8ZHxxDQvYX7CyWA.mp4
49
- Copy of SnapInsta.to_AQNDxACl7-Kb1UXEmVQHTS4ufaN8Afre7gRno1AUCsj5XeI6uN7x_s8EscnbF86W0OrFPyv2JZzbmCHDYBRT7MeiF0iZaa9i76R5MP0.mp4
50
- Copy of SnapInsta.to_AQNc3jc2lOqwTDozp-QRNKmgYLbLoGW3wwBlcwCj2cyLfF0SthG2lvIRqrcTapir0p-c_rF0MvETSWCHKiiEReZeQSri4bWUMTSmqJ4.mp4
51
- Copy of SnapInsta.to_AQNGgW96uyUqaSEmojIIOX0-UFHi7pW6UMtd3f-qYQM3lVCws_ZemlODQuEvGffnT6oKEurN8XAlVeLCIx3K1JtM8qQ28W4Y3dSiojo.mp4
52
- Copy of SnapInsta.to_AQNqgyC6T5RK091QrV-yHlM4GUp4T-QE1vmZfCuUo9l5xE_DLThc2HM8sGlQVE_YvD1CZmUf9Wr9MlTLR-xmWJk3htxgkbmCMTQWc_c.mp4
53
- Copy of SnapInsta.to_AQNOkqj1ynj2WlTnixJabKn_mgCymzAsKd1X3tK8OxrpQMebs_Czq1DIs4NigcJxMDG5fpxj6cjKThJfazk0I7Uf-u0w2A0LP4Vce7Q.mp4
54
- Copy of SnapInsta.to_AQNut0rydUv3waZcTnQknkw25NNry23Yk1a5pxPawOlUHv-1RfwL_GX7lRIuqAMYAT_NBu75wFNmB6-lUcDRfQsyd9AOYGlRQ80FSXg.mp4
55
- Copy of SnapInsta.to_AQNp_q8cMWZYmqj3ES8ywSEz7KUE1XveZsoRP1D2mOsdPlacBIFepEdVy-wLuecGDXXiQZleiE4azP3yKOv9o8rJ2BvvsnrIeMnydv8.mp4
56
- Copy of SnapInsta.to_AQNVXtkcIv2jgFgF8i0hv8lLiXZQY9DBkodLrSptRyN0jTzWZhyjGpnNMuRyA95-swPv3fa_BDeYxXFIHqU5W4SohEKe2I802YdyytE.mp4
57
- Copy of SnapInsta.to_AQNmu04hZ5TwfGCjLo9MsdrvK2dPwIwWD8RRvMUZYk6akhqwDxudyZalPk3vOYhkOBcEz_KqHuFK-7LxlIIDHCZRwbUaQBXdOnqPCuM.mp4
58
- Copy of SnapInsta.to_AQNUGQ-NL5IQA-gvcuYPlcx3aowuYZQt_EcKIXMcFPnKJ-0T9IjYBcl6rgakbO6vY0LhdJMNLBtTzlZLe86wWSKzTCb4B2LqewnWGuI.mp4
59
- Copy of SnapInsta.to_AQNszjEx7PwMtjaC4qeLorTbOkEimHLVkw8D6dT-fVwRrIIIvjvafklpA43RrHRpVtXC_p_-en8lvK5iullr_TMCA32WTv1WFUsL6ww.mp4
60
- Copy of SnapInsta.to_AQNWwkUk5S8JdEesH7oLj2epicI9ssXWM1-ElWakE8p1kvFPqkZJKdKGMMoqUTS99msAYZPjoQlTWn8GDX8eyL7FrhAAG_7rMIRoXF4 (1).mp4
61
- Copy of SnapInsta.to_AQNWyG-q8U40HB8VghttRXiZH_RcC3sE7HS16PgCm5blZrm0473hYDNqYfgIM8fy3BVbcIItU7f5_Ii-3ssyXudzJItoEAYb2vNsjso.mp4
62
- Copy of SnapInsta.to_AQO5lxJemg0xxKMcCtHYpQUuWi1qV7K_SYDcHKtnT0BaRUCNQiCDd5xIehsj7m6zUc1daW4UNyJO3SbKUUN3g6Vv-CtCKuMlNo3ysQI.mp4
63
- Copy of SnapInsta.to_AQNXee2QFX74CqAVz1z3zycA4qCG-_SxNEPXTK709yW_9MZ8Mj413gp7960SzKsmdUKWQ_8vnTOalMJKSeXRQ8_acDzwyM1CWMtcfDA.mp4
64
- Copy of SnapInsta.to_AQO2tY5oxXgt--yGzdMZ52TaqM4n1kKdgYQobksNPHGS3RxZDwW80dfGpGOvzbZxTDeelZHLW9gqTotCZrXSAmVniD6q548qYLevh3Y.mp4
65
- Copy of SnapInsta.to_AQO6ll9jrclsLIy48DIsGgQsPJRsvjlih7sNh1xFhYaTQilEXR60NeLu7C19bD5yNbmWuGk1m5FlRC9DiIe3cTn0JoPoWCdO_LQwo4Q.mp4
66
- Copy of SnapInsta.to_AQO-IK36J0ApycdOPHaErWggzSqSav6ad6S2NcOHtzHaxwUctMYITAcDe6YjNZUSSfMiUCefYNxthbERC4jfOjVu.mp4
67
- Copy of SnapInsta.to_AQO8UnRg0-tfvdgsR2ewKW5BPgTJQ86rDzyI8HXJ8M9aOLG91S2GygpAhRrs7Xn_1h5NVkRZIJDj48LUoI1MRlOsCx8guaMQdCIgu6o.mp4
68
- Copy of SnapInsta.to_AQNZHnrmkbNK6-fbXQhC21kitv5bWCI-jjvtOouIYnl0iU2vBQhxaMb8mpBBZp2928rQLJRmnkQy7yaijwoBAoy2ej0-JGJGYRX1fqc.mp4
69
- Copy of SnapInsta.to_AQO5grbn2BghaX2oV7l5-gesUc09Gazts3wqwOHPCdYpWCOiXBGqL2Zgh57YW2G9YizAesf0d4_ELAJ-4IGD-TuJGdtcf5HKV41oaqM.mp4
70
- Copy of SnapInsta.to_AQNxzYITSouTSZE72xVRvhn-7nF-174LX5CeydRJkv1yfQjhkBsFfc7Ig38x-ui8majkxOS5usBNIys260X4sHNIphG86lGMZrw5SiY.mp4
71
- Copy of SnapInsta.to_AQO3UrFoq9DLdKwHZzfkK68_azqA2XHaRUTCaITN1b5xShkPVSWNKy3IhJvoV5PF8r-9O1IVD_91_tBzIz31-QMrZZkSB0clooDKVWc.mp4
72
- Copy of SnapInsta.to_AQOie47b-ZDnRGvuNsY9Qa0-1oWqAG-pOLUDe3x97s7J-6a6bqLUkrB0kpt0tmxm7ZRvHF_DdpnLSd8jzVjZG4YsNRTLWemBPtOYLSw.mp4
73
- Copy of SnapInsta.to_AQOCNHXtxn2PLTUGROn0g0dlKxFzOiRqy1EXVEGDdl8Im8lMFwcgNxGV0RMOFn8n44REQJi9QlePITdhndS78HnfJh4k0JroXixZvrg.mp4
74
- Copy of SnapInsta.to_AQOJGnxFeVsBL3kAxTI7p-CFwep-5nCVy_co9P8nfdqIJDXdV-odp5Dj0oGQBiOXL57WWGDa301BQcVavdr8oqudZt-834FNelqixQI.mp4
75
- Copy of SnapInsta.to_AQOHdlWMSmXl7W6ZAQS_RIgCxnZ2D22aylN0A-A1UdnInZRbfaggAGfAPyrUiSuGe4rsaC9LXJGdBASlUVxWnMhOO7ddcDcwunGZvBA.mp4
76
- Copy of SnapInsta.to_AQOj4u3P_qWSFg2qQFbYoj9xlakd3f-tPS7zVo7a9uI9MaHiB4jgrRXuOmh2BR_cWgyull4GbKUyLstCREftucbmcOo_klopgD9rnb8.mp4
77
- Copy of SnapInsta.to_AQO9jZYb5UAS7vczJ2rx41VV07Kr5fevPObWU2Xy0-JEtdXbMgkY7LiZTJALUN8a2VKdo0ax-Ru5LjDPH9NOxxAk3-NrVGCoh-Vwx0o.mp4
78
- Copy of SnapInsta.to_AQOf_smlyh93ARb1rFi6qbjFbX0mHeFxg3NdxCqTnXLtyet8Br5z0B_HMnWnaku0ZGxI-bT_2XQUckDM1GhAalf9_2Fwu_uUDa09xBc.mp4
79
- Copy of SnapInsta.to_AQOgTMoI3xhfDVrDebf3l-OpvecPqspfbdAQq5_T0xHM15YaNk6NWglFj1m2TvTZ9Qul5wvPuhxAXbVVc9EbQcmgwjXqSgcSODYDrS0.mp4
80
- Copy of SnapInsta.to_AQODK099MNYBeoOlkpGuxM53q_GE7cMiuLbUh0KoSdRYc56W0Hy8vfmN4x8jnnFC6IMVwQbkwg7Q_gCkprXoxgBULKnx1pisnezw3RY.mp4
81
- Copy of SnapInsta.to_AQOcxQ8P082JwhtQjzTrcWZ-kWEf3wU8yhT-DYOU054-aSgRC2QvYQHFfLL51UpKkRanjozx9VE-1ZkIh6m8vuu6slN5DiFJe5L4VRs.mp4
82
- Copy of SnapInsta.to_AQOq3_HJc8SgMrfMC-sxrO_Aim1g2vF5COJShA13XGq5orlJv5eE33Sp0Z3rwAkFHNoJG8_f-Uu-GJZ9vh9wv0zoZN1NPomttOqlWCg.mp4
83
- Copy of SnapInsta.to_AQOrCMLuaUT1sgUL7IOrGhJ1dFZnhwgylXyh-u9bKoOBw75kyM2ckeL94XdzgYfUAAGFGi_h3257Bmuy47oVb9hWknzYBQp02P7XV08.mp4
84
- Copy of SnapInsta.to_AQOneOr1SWdVeH4FvnvLJ-ouL0cLN8o9hhSDyh7A37xYwnIAN6k_P9lnw-7Tf17ofoTsRcYhB2Qvw3PCmrzO5dw_77PrmJyZvKdmdvU.mp4
85
- Copy of SnapInsta.to_AQORAyQ2TJnXZG1_231tzfiVS_3Y7_aM0-ZKLicxq9enBao55uWKI6UifhevC5I0Shs0cPXUoUccz1Oiz463yTPEzZ3wq0F9xEfCNC0.mp4
86
- Copy of SnapInsta.to_AQOKcjLcHZJfYU7Fs6SpR9DsOVGjrQGd9isJv07z9sGWxhaYH_tF2czjXziGLlc1EyQXh_1vTF7QaaZc7vipBaS5r_XW_e6PWx8qSmU.mp4
87
- Copy of SnapInsta.to_AQOmSXa7DPKqoXcT56EWhcZSMUJEKp7a8dpCVr6nKXml_NY4coN_575FGejR16k0vHyv9zcilZteBmncUp0dn14qigkl7SaMtsz-xlo.mp4
88
- Copy of SnapInsta.to_AQOPrWx3BibShwkUNZYkJ1xiI1MWakSn1Esd6t15vmzNNgeZp9XCwSUgTkP1fxn0AVFU8bDMv6bFVjna2uvfEZP6ybC5HJsI0-hqhWo.mp4
89
- Copy of SnapInsta.to_AQOQkN01gcWn_9jXfBT-Ctz85NChZalDyOVAC05lpUAg_wfXBYWvCXSNEoEQ-F72Oypyo0S20RNQ453ZUPGik4r9xe-f5qsMwxZfwvE.mp4
90
- Copy of SnapInsta.to_AQOnSzCRVuAmb_4edTrHhUHmWWL5Db2xkXAPjASJwpHbu4zsxOjwfsLSuoApDurgl6KLwGflWD97AZ11JMv2bWrhJ-Y3jnPAwqDRwqU.mp4
91
- Copy of SnapInsta.to_AQOMN-1ZV_U_1s8JPnQA3ZUpsNGNAJEnlPb100nLAvnNAkoGljAi7aOPUbp_JjwBlvQdzGvXRBlioJAMmDCk8tcYoaSWqE-2vwKpL-Q.mp4
92
- Copy of SnapInsta.to_AQOxJlTZQkNRwRmh_l8_PB7ahjQc7wxemPiWsfRocdqxwR1G6Pfx25LVDG-SkpEK6IZiiFjC0h_Rre6YNN3abvxKp40JvmT-5f09yxo.mp4
93
- Copy of SnapInsta.to_AQOS2bo1S3Q7iDN0Q8hwMvDFMNPN1_ceQM9uLuUeC094f3bGWuStvdV85KUfb1E-V9FTM-UiOcvutPjbx4zkrOVNLF0B3HzNkRJXnZo.mp4
94
- Copy of SnapInsta.to_AQOsYP-A0MHz7SwjjwagDRYV0YJ2tFfONsyEYETOiQWqbtDL4xrDovzIHz-S1DBtGZ4ywLa4Uz0-29tnG4Ha-PkRscxj-h2TPuwCxzE.mp4
95
- Copy of SnapInsta.to_AQOXUoM-wwRwqCi_jgC3Bfi2LtMzhB92460mHz9-_TZmZy207wSmDkCtS8tRm5AOigV82_6UprIIqxjIUvsMv48vdjcy9RJI24mU7h0.mp4
96
- Copy of SnapInsta.to_AQOtsi_R30JnRh30gaiV5Z2OQyI20Tuwttk5bw8r5gHfWEDSGu242zBej87Tq7W2naCXzo4BjQQTolzm2PEnaCQ4HJz6NGyNcOsyNJA.mp4
97
- Copy of SnapInsta.to_AQOTr6BZLkDl-El9L_fLPbYN8RIZmt2C1jMS3oslwPd9MZXz_yxqxRiJ1VeRAuJwjNw0BrPpXpV2-F-olaJ2en-J4RlI0vPl6zHRilE.mp4
98
- Copy of SnapInsta.to_AQOx9lpmtsfj6erRU681gfcRo0LtoBenK8TEm_2i6_TSqYfJd_4JQjhfSchJLsS-Dnj5E9AIEF6QG6J3oiThCBrE.mp4
99
- Copy of SnapInsta.to_AQOW3VYdttuKMHS9dnYNNQEMMBOr_K3Ky1NGQF5B7aB_8P6HAJnpZ0KOAj7vO0i6MZKIFmpgDPDzqAGKxr-6Nen1IEE8ftMwA5DiH5k.mp4
100
- Copy of SnapInsta.to_AQOyu0xEyFQRxB1moDAiTx7P_WxsevMzla30_wuvheLERk12Sy1UpQd_nRbbUsn81CCThO597ZY1shllccADKQ04J6eWBc1MvSqHTlU.mp4
101
- Copy of SnapInsta.to_AQOVuXn3d0cfBOu0vWzc5sqWh_lt1GQ68VeH7IPu-CSClifisPa0O7D6m8n3gW5Jq_WORFD3Gq4yxOs0y15pOeIF4u9Spc2XYh5NHCg.mp4
102
- Copy of SnapInsta.to_AQP3nzO4iWcJJW929MdCfTuADyEtRel-x2Z6KJRcdYgYdsmG-xazz5pJOyPcnXmakaodd8aU5aWdktvYNV5nqDgj0o5iSDP65qNKtcI.mp4
103
- Copy of SnapInsta.to_AQP86DAQoIZCVJiTrrG6nfsfHO4Xz4uZT-mjLDEaf6P1paoKFkgN0SyR1SR4lCcc7nJ9MOh0hszj9FCMU9UNR75gCaaQcBJmOjUxLTw.mp4
104
- Copy of SnapInsta.to_AQOz5JrVeD8Any2OdPur3JUp0i5W9aTBbJiZv43XXSWyoC8U8o3Yeu9ZoetZrIswlJzcGwTmAGN7QhF1cAp5XKbgPJ5z_URSxGzX2lY.mp4
105
- Copy of SnapInsta.to_AQP9spO2KiP7V0PE_SD543-lOaZlmts_ilRgpcTvLJZ0jY-gA_FBYyVLgFw9hxrSxyiku2b7hVR9yLuftcaeLcGMnhfkapC-_sC2jQc.mp4
106
- Copy of SnapInsta.to_AQP3XJBJez4qSqLuvRj4TLngrQHf3tSaMbRfpCBPCFGohMMsaIbm4Asm6NThIfTKUUedkl4m6EaSlS0hmW_MOH2FKzMThfWvcdhhKA0.mp4
107
- Copy of SnapInsta.to_AQP8JXKxS3ep9SjRn7QtzEP_9NHEnp7F_CheFX7l0DJw167Na20HajhR-5pYkgcjsNsE9avOKy_wf1e7gt7UWQXhar8oEfEuJqnLP6Y.mp4
108
- Copy of SnapInsta.to_AQP-84cnFxBU3ogW5aYcCZxNnk5Npu06VHdtBiXI8scgit9Z1iF3FRJ85BFhX2r8yvDtq5CogzDYvlMFUCi4X_ap.mp4
109
- Copy of SnapInsta.to_AQP6fOo_u0HjG72G_f1L9m7AzRyA43o9eL9czREYfO0iooWLpbjJL_uiMlqJcBE2OcqLzvoXNpMydB5iisdnIGmeAbHZlwIrT6jJHh4.mp4
110
- Copy of SnapInsta.to_AQOZBSVlF56HzS7TdXQko0MAjNEWCSYzvnJxFlLMDx6-yjhdu6wp4V3Sim_KZr2kAVcyaLB99VgcNHSjmb-TNJdiV9_Wxo3Dr9gqnO4.mp4
111
- Copy of SnapInsta.to_AQP641BeVK_fcPYJTquNq2RSmBmrcpRmMenS3Xbo0Xcs9DeiRvOVIvmi82Hksjc1op6N5oDbyeVk1GXN5EfLiCCi8sUY5lm2Ylos9Dc.mp4
112
- Copy of SnapInsta.to_AQPKvI5tzKt0rcYaoB9HdmbC-Yi91trtjtas5xAVBur6tGw1stv1d_wvF3KpxZhiuuz-N5ehEQvJs2QW4Wfjg8DbKiPCgRXAUxesOvA.mp4
113
- Copy of SnapInsta.to_AQPdKMqmTouNVR32c4lic4a942XUPm9mbO3ert5cbn4TPnUBGcz20F1qexD-HMhsvfO7S3Q25Uq13JEJCZ_CTXoXLoqFH34h2Zgw1Kk.mp4
114
- Copy of SnapInsta.to_AQPBh6ZbkOi9DdppSSZqeMIYc3bb_tGjsSZaa9AWvmD772lxzB5x3ZqHrqzfP-ajsh_tcbjPPxTsMoLWvK96XXxYp3Ajc0H8h77JN4Y.mp4
115
- Copy of SnapInsta.to_AQPGtsWjjHydzFunbj6-_MAO3ZwrSLXHSgfpTxeq16cIFGVAsfc9SJvVaQxqs1K3C6JfZ7_C1QTckxR5x8Sj5r6Wbt2Mbka01e-bw-0.mp4
116
- Copy of SnapInsta.to_AQPBncb84_kFQheHBdy548GpuIMZRuHQPs0E3jqgOJCXiMVuSuDhQi0TwudmRwRLL9b1T7t9XozpTly6FZjT6tSaVUm92Sv0yFKfmkw.mp4
117
- Copy of SnapInsta.to_AQPBqVBlulo868CCerAaOgobfWOhBeo0w-cuTDWwEwI0WgjbnyU5iLDezQh7qe2tMQd_zGMowtF6QR7GywTdjYSS.mp4
118
- Copy of SnapInsta.to_AQPkLtb5YAn2kL92TWPUH-6tpQax5koCpXQOqQxJr7UbOv4YXijRF06un14DfnMDtbggg4ek-s5-OxHHIK-7fYStc2e9_JeLD1UTo5Y.mp4
119
- Copy of SnapInsta.to_AQPfpVXT3sWPsS6EK8esdIUBU_cY5vhS6-Q2BbBXg7-fEnJnErhKe9tVxgVDuvMC2Y2edNUxwWrCpbHceINf0LVM.mp4
120
- Copy of SnapInsta.to_AQPDhbnoXg-EkALbokeGWoAuClFYCUlWQbr3ZAk7DVKMk73B9w9ZRuCgsB_Kz13KnC5bfhSqG6pHH-x1HenHh7vEqaP9MW2SqYSEnn0.mp4
121
- Copy of SnapInsta.to_AQPjNgNIELsBfrGYbOhRaJ-r73lHr_f6wMgrMUzrPW5qya9TFMTy0Ncz-hGjfI6PRdjVpEGs2_UpJbagXBrDRGgma4YUTzFstKZflXQ.mp4
122
- Copy of SnapInsta.to_AQPQyIZYpsfOCAtthorRzThoegE-DO1r6YMdd-44QNcGM3MeRhrKwtraac3HesG3yRWGP0SqnoZ7cQAWRUdVGUHatiZQgY1ggysi360.mp4
123
- Copy of SnapInsta.to_AQPS75Q24F8RlxnYHijqrtDBGkscB-JI8s1gvFoyUyeaYWgDuzQz_hF9hSJ7d5u5_-CJnmlVlDxQOxENCuZp-EgsQJunXvqUmtsN3Qw.mp4
124
- Copy of SnapInsta.to_AQPmziF9WU9q7w_9TF0a8535xUq1jqPxmq14zGHGZnWyTmR4ivlTiLIck4r2OTdSutO1DsV4oZdS-Zv27pc3VUzGIB1kjy5U8R0s2cI.mp4
125
- Copy of SnapInsta.to_AQPqehWNSvv3rYQCvk5b7O4Ld-GiiOxXuzuLss-QK-0eJ56hupiU05n1XWv1vCrrDiOIwvRCQGs0gmNh3Nc5Battl_tgnKn8lNrgKtI.mp4
126
- Copy of SnapInsta.to_AQPsXrX7q4aXLWlsC6VEA_BFMaEWb_NCp3c1hBm4afw-_iD5YLj8ddtPgG3MWO7HVNkGqFvpdEWAouP2hK0VYOoFUsnM4Ys9t9W8WWU.mp4
127
- Copy of SnapInsta.to_AQPOi8TaqmjgR9JFBiA4ckk5c9VkzxKQanKHXuDyG5SqSQBJIqlVpArjpCR9HRc-wtS2Z0Sb1gLwUayx68FRwOckb7CB-yog-otVaSs.mp4
128
- Copy of SnapInsta.to_AQPNbBZCVl0GNAu6VP0717BmrCvatqmfwUG1xyJuUHs4ikpc53URO153T1JgI81DyWuc7z1PLnUNdNdO9v7tuvHmp9fhShPCZmdGsfg.mp4
129
- Copy of SnapInsta.to_AQPLPXOUYpJeVo3hu2TDcYngTealPO7JzZ2FbUWJSeoRcJV1Ij6ll2GLBowRVtTPXl5ROA9qmUC-lXyezdINjSU9xyGAnblZSG6eI2Y.mp4
130
- Copy of SnapInsta.to_AQPocb0pCM1_RGF9JX4avvuOmzHrlwYHar2zYUAhb_fV50G86tKb0aUneLvyUHTPvnI9iyOwu8YepoE6xY0NjcG-v5SNy6S27Cgfws4.mp4
131
- Copy of SnapInsta.to_AQPv77l86PSqUsTE8lQdViDoTCH8AKQ4u_NKmuDrPN00tfZkMaWd-ki6PKj0AXxFFv3YB0h2KxYFwMpcvLKyVI2W694E_sASTb2dN9U.mp4
132
- Copy of SnapInsta.to_AQPy4LeOK7HGMrdDGy6UupOaWCfNvxrCoEq1sQ1AmWxtt-wAhh1lyRDbA-iV1gDym0P3hjy9wUPUEFbOYjvONEfCMsCmQjzzzMxAMPM.mp4
133
- Copy of SnapInsta.to_AQPXKnrDd4o4TWs8IApXii2FpHkLK2pVc-ivIzccjkTyKogfBtEKF3e5Te0agwsBrlrLbtSI3YMzvkQYJs0E3iqS2avOzq7uSSrfJvY.mp4
134
- Copy of model.mp4
135
- Copy of SnapInsta.to_AQPYpHm9LfSCZ10HxVHA2zOGVAICYZdlcbj_2ko240t0uM1wc4GrGDyvabuqINLZ-z9GlRLcjCrOYntJx3NJb1Cd4ok4mNC60Hqgdss.mp4
136
- Copy of SnapInsta.to_AQPXflYRuO1l5hRKDxz7JIxoNs3ApCXdjPaPKb77EGzxoymI7ig1FhkAPCSrCOvu4eQHlfiNpLva2xmmcL1K9SHg60-IBip5xkGasmo.mp4
137
- Copy of SnapInsta.to_AQPZ_W5eGgta2gCBDMq5cVMn4hS9y7LXlyOp7lBdNMWvRUsvFDtITjdAgnK4tAi3JfgSihgvor_Yg65fkdW5XJ4uNFLlC9ovBliNA7Q.mp4
138
- Copy of SnapInsta.to_AQPVeSGnntyRxeAL1AK7KAnKsHu0UMCWMOjcn12kVQbcHTrMdn8KtsqvyWY0d6-53x77X0D0zoJIx0aoJZJQO-qjcOXmgN6P-jFB47I.mp4
139
- Copy of SnapInsta.to_AQPzA_PZlELUWUWo0tm-HSoVU2l-WT3iygeZjwyyAH7cE1ykYxkpUoreWPnHhNF2XxhuY12UY2-m9V5mJHoYNg35C5rvSDWivElpf9c.mp4
140
- Copy of SnapInsta.to_AQPYH_-zf8tTP8LwktjY-dpxGIsmWPwyaT54UpartRzMU_j2hgnvcI0K23mFTfiCNkOaRbH1EvUdLiQLLvi62FLCSy7Xz_gJHERlQAc.mp4
141
- Copy of SnapInsta.to_AQPVjEY_ZzxUJoWmIsLRGClhacfUMMfLGDTeXxUqOZwg_bcy8YrxVBVBs98T2e8_mKLhqyMEDZyATB6TOMGnBjSOegdCxMZILWKstjo.mp4
142
- working.mov
143
- Copy of Balcony day.MOV
144
- Copy of BALCONY View.mov
145
- Copy of Bath.mov
146
- Copy of balcony night view.MOV
147
- Copy of chilling at night.mp4
148
- Copy of burj khalifa View.MOV
149
- Copy of Black and white house interior.mp4
150
- Copy of Beach.mov
151
- Copy of Beach view from balcony.mp4
152
- Copy of Beach view 09.mp4
153
- Copy of billiards interior house.mp4
154
- Copy of Bedroom.MOV
155
- Copy of Beach Miami View.mp4
156
- Copy of Big screen & Playing.mp4
157
- Copy of Christ Mass Vibe.mp4
158
- Copy of Chilling night .mp4
159
- Copy of Drone.mp4
160
- Copy of Dinner outdoor.MOV
161
- Copy of chilling balcony pool.mp4
162
- Copy of Dogs & house.mp4
163
- Copy of Dubai 02.MOV
164
- Copy of city view.mp4
165
- Copy of Chilling Night & fire screen.mp4
166
- Copy of Chilling.MP4
167
- Copy of Dubai at Day.MOV
168
- Copy of Dubai at night.mp4
169
- Copy of Dubai view 05.mp4
170
- Copy of Dubai View at night 02.mp4
171
- Copy of Dubai view 03.mp4
172
- Copy of Dubai real estate .mp4
173
- Copy of Dubai view 09.mp4
174
- Copy of Dubai 03.MOV
175
- Copy of Dubai view 04.mp4
176
- Copy of Dubai view 08.mp4
177
- Copy of Late night view.mp4
178
- Copy of Fire chilling & Pool.mp4
179
- Copy of Dubai View.mp4
180
- Copy of Enjoy View.MOV
181
- Copy of entering home.mp4
182
- Copy of Garden View.MOV
183
- Copy of exterior view of luxury house.mp4
184
- Copy of Duubai places.mp4
185
- Copy of Empty luxury place.mp4
186
- Copy of Luxury home 03.mp4
187
- Copy of luxury beach.mp4
188
- Copy of Luxury bed room.mp4
189
- Copy of Luxury bedroom.mp4
190
- Copy of Luxury date.mp4
191
- Copy of Luxury balcony house.mp4
192
- Copy of Led interior luxury house.mp4
193
- Copy of luixury pool 02.mp4
194
- Copy of luxury gift.mp4
195
- Copy of luxury garden night.mp4
196
- Copy of luxury pool.mp4
197
- Copy of Luxury house night.mp4
198
- Copy of Luxury house 04.mp4
199
- Copy of Luxury interior.MOV
200
- Copy of luxury sunset.mp4
201
- Copy of Luxury pool night 07.mp4
202
- Copy of Luxury pool night.mp4
203
- Copy of Luxury interior house.mp4
204
- Copy of luxury view.mp4
205
- Copy of luxury scales.mp4
206
- Copy of NY night view.mp4
207
- Copy of Night View.mp4
208
- Copy of Malaysia View.mp4
209
- Copy of Movie time.mp4
210
- Copy of Night chilling & fire.mp4
211
- Copy of Luxury yard.mp4
212
- Copy of Miami views.mp4
213
- Copy of NY View balcony.mp4
214
- Copy of Morning work & Girl bed.MOV
215
- Copy of Morning View.mp4
216
- Copy of Pool and & View.mov
217
- Copy of Outdoor Pool.mov
218
- Copy of Pool at night.mp4
219
- Copy of Party indoor.MOV
220
- Copy of Pool view from house.mp4
221
- Copy of Pool Indoor.MOV
222
- Copy of Pool Sunset & Budda.MOV
223
- Copy of Paris.MOV
224
- Copy of Paris 02.MOV
225
- Copy of Pool & Palms.mov
226
- Copy of Pool view night .mp4
227
- Copy of Pool view.MOV
228
- Copy of Red house interior.mp4
229
- Copy of Pool view night 02.mp4
230
- Copy of Recording At Balcony.mov
231
- Copy of Rocks & Beach.MOV
232
- Copy of Pool.MOV
233
- Copy of Rolex & View.MP4
234
- Copy of Rocks & Beach 02.MOV
235
- Copy of Riu Plaza.MOV
236
- Copy of Sunset Sea.MOV
237
- Copy of Skyscrapers View.mp4
238
- Copy of Sunset & Friend.MOV
239
- Copy of Rooftop View & skyscrapers.MP4
240
- Copy of Show View Night.mp4
241
- Copy of Skyscrapers view & pool.mp4
242
- Copy of Sunset & Budda.MOV
243
- Copy of Rooftop View Sunset.MOV
244
- Copy of Rooftop View & Pool.MOV
245
- Copy of sun set chilling 2.mp4
246
- Copy of Swimming pool.mp4
247
- working.mov
248
- Copy of Indoor & Pool.MOV
249
- Copy of The view from luxury home.mp4
250
- Copy of Sunset View 08.mp4
251
- Copy of TV & Chilling night.mp4
252
- Copy of The luxury view 03.mp4
253
- Copy of Sunset view 01.mp4
254
- Copy of Sunset View balcony 03.mp4
255
- Copy of SunsetView.mov
256
- Copy of Sunset.mp4
257
- Rooftop View.MOV
258
- Copy of Wins & Outdoor.MOV
259
- Copy of The best view from house.mp4
260
- Copy of White interior.mp4
261
- Copy of Working & Sunset.MOV
262
- Copy of Working & Redbull.MOV
263
- Copy of Working & View.MOV
264
- Copy of Walking outdoor.MOV
265
- Copy of white luxury house exterior.mp4
266
- Copy of LuxuryItems.mp4
267
- Copy of SnapInsta.to_AQM4NKne606GLmphwWDjSOhoRmUj_GjVyqIxP4-a-hP-W3yEDsJQXMxSwXsldB5clNSr6DSITR27TFyeocTM0oj-tuWaXdoj9j-Zl0Y.mp4
268
- Copy of SnapInsta.to_AQM24IC4X_a3aNzH0LEq_P9up2hQh2ez6TS8wf7L8O220fZrPfK1jF9Mw8aHvy6PRq28uiwxtbYTCEnOdFG3WDAGKBH3UqKMGMK-7pM.mp4
269
- Copy of SnapInsta.to_AQM4uutpNjEmK9bbNS-tcCicGSLyLxbxOZ-XZqMAaHqvGy6bzBR0jxYXYqhWFW1yuYBmFDR8lIiNSUk0nLH6QBm-CWF3Mv2OXjL3dxM.mp4
270
- Copy of SnapInsta.to_AQM-NfgsLA5vXDeOlarSpzelq4kwg-kGgzi4yJP9yIswUySb3bOYm6PHQlHssv7NOUiDPjNlVDb410rCx7k4KFTvlWFekhsevwl-Mp8.mp4
271
- Copy of SnapInsta.to_AQM4M3V-O8H7cwOFee_sBy5PRIRZsHp-cB1ZPQCccPaBifuqbNtvvaQkBBW22cWMGwzRH9FTaPu_GEthSX_zEI728ODqtP1qYJiTU_U.mp4
272
- Copy of SnapInsta.to_AQMCvzBzgcdY4Nf0srYDZQtth_t3iqsyOUdYHpH9heyKk1EKEGQXK6bZttnwHcOdG7HTC_uFC_dU5OH2xjONPdI-v5kGmObTT841yT4.mp4
273
- Copy of SnapInsta.to_AQMepEVWahHMyZ19UYw-dtCSdBW9My4uawp5dfqJ72SjTK5lNiFMRW9mMIPHTHD0AgcgXuS3gUQ-lyBaDnksdQeaKBaPKOgbyQtF5kU.mp4
274
- Copy of SnapInsta.to_AQMFlt1XquFOvRAyXFUAr8gki5RDa5ADpH_q3XIAUQwcAldd2fOuGVyHVut3JQ3q5F28gLI0K0SJYXXbpVhQilCjW33_xp9aSrukWGw.mp4
275
- Copy of SnapInsta.to_AQMiDPhW6qyqiq4W2cKsy1zmD5lrFV1FB4mgkdBhBJ5uY9aFJO56E5ALfsNMLGuElJmUqrT5SL4bomjO9Yw0bPqF-247_epJS3-AiOY.mp4
276
- Copy of SnapInsta.to_AQMab_NtxU2FPO8jE9hAuV0J3SLw0YzkQYLP3bmJcb5-dYZoy9i0AWJVFOyAvJ4QENfRHpOZSthu_IEe1RFp8T2BSzli-K5hrviNPeE.mp4
277
- Copy of SnapInsta.to_AQM499qNlXGRSCpF13uZGkL1cVUzwDf4Sk-tyS4Bzbyj50QEYrlRNIweMqHwC4FG81wfNq3jvX1-NC7ZjJwh9_2tFMtL9JdjyRkMhZA.mp4
278
- Copy of SnapInsta.to_AQMGL3eaYBQQD_qD3IX_ZQlHwVphF51DYGoN8u9BVKY2Ni69lENNUOVwEB6XcI5OM3pqz4O9S3iZnLIe2RYWCAoFBPt9quEGGFfvguQ.mp4
279
- Copy of SnapInsta.to_AQMC2N_TFCxdBUJDh-Xcn7cmFL80F9iYpVeVa9SeWqkLcWKGkf174a67qnotlwfGpO2JimthRN47dW2cnjU8cTas-Qyk26SnAqoqwHU.mp4
280
- Copy of SnapInsta.to_AQMGnwHh5y9BNFpMGpzePVfubsN-37uamRWIExALPL04R-GRuQO4G90cJEBTAwq6Td0T7Yxmwm5Q5z1dKA7ls5v55KbF2GXt1IdFkn4.mp4
281
- Copy of SnapInsta.to_AQMfGnrXUeQij1G8e-ZJwg1Wk8uoi_meqlFIHja1d4_lx6g-0PTu92VmDDVUHiXdS_qpHC-i_EENGfMRXJCXJVn_BVs9Ce9l2acNGvs.mp4
282
- Copy of SnapInsta.to_AQMslPIzohCu9Y2L3inGPkaBOwcxafqCyXCawXTgSozTXPMYJ0gkCOYtN6ApH5PgKofDRHcBjJsIozSV4k7AGaIKEUh1q_Zxal510aI.mp4
283
- Copy of SnapInsta.to_AQMrdDyG3MCEgIQ94Nb0Q8paE-FDJatGd9oOyRHERhZE2tjkteg6u6JqmmN9AoiZbyWe4KZOsn3KOD5A9rrUwugqPPAbha8yLE-T2_Y.mp4
284
- Copy of SnapInsta.to_AQMtW7oVDta7vKeIV0nqMvEJVCsi8rTIwn0ekf4TiIlp4J8RgKGAQj70yTW1lYNzhWV4GudvP_EWoozExaZ0bT4dJ_KiAkdqISr3i0o.mp4
285
- Copy of SnapInsta.to_AQMnpQEoIBNiTuR8GRzGYg_o_d24N_QkHrr3dP-vweAF2rxlEFQmFCe9TMZImP0xsd83FW3JyYo9JW1S_Ah8RSx9g3Y9R3DSwPt3BQg.mp4
286
- Copy of SnapInsta.to_AQMsNF51_pFEZwzGoh9gTxFd9qtu5xi_oo9g7PB50B347wksGbh7QPbP0suBH-bwnoc0ae0Lu9MaTESbTS6qkPnuByttADPTjGmLjDg.mp4
287
- Copy of SnapInsta.to_AQMqRBkzYseE4drD-7KnGwDSh1Igz7mv1U7AeMzB_B5-vuYXvN3k4Lbj4TQDa6UigLshgSHipVMxd4uOZc_gK25SnizefSPiBoccfkk.mp4
288
- Copy of SnapInsta.to_AQMQrWBgv4NuDgJ6YdBsRG3a5al7fdCw6Jfuub3nPbeXwR22pcgGLhA_AKZvW4JJmUl4SAxTDyoM1dQvxpfFFf08JtEAVH0m8Vzczls.mp4
289
- Copy of SnapInsta.to_AQMqMRlhRGUWlDraQ8ZYtfmTTgSLFqQSadmbpMYiDLP84yOr512rgQ8t9-3wyHjeX_8_QRrtcS6qiqhw_-nzo5wcoAU7VSjc9uqnIGE.mp4
290
- Copy of SnapInsta.to_AQMKqmIaUkOynWGIkxq2d3Ci32V-YKcSaGtB0pLxCcK7Qe3tGwBICFN4myXaGC6dolgfF8JL-9knVx6nCC9gztSoH9qVAqyonp1pAu0.mp4
291
- Copy of SnapInsta.to_AQMzV8y0ZJta3mUnoaoXEFxE9Lsi-0fXZ5CopNS_uJ0yz9WuEjpqL46aVmvjZqchS9vto6nhE8esmFpVLUTegzozLMoc9_JBZ7aA-hY.mp4
292
- Copy of SnapInsta.to_AQMythBH9Cw2Gz30cZh2c74mDmm4SGud5IV7SVz50d0QrFEOW1pj-9OBYvuWNHBnCjV0hC2WLYhA1uAWYZ5ihXJXqR1527MZIWpN79w.mp4
293
- Copy of SnapInsta.to_AQMZuWnJCLvbHZ3Q6IkL70z5R219v2aBvtCdFkuMcmLkhaA-ipX-DWHELgmVSb8gl1aSjjwUn1wvwWJ_BNNxAdxBxhllqy-vF_YPzKk.mp4
294
- Copy of SnapInsta.to_AQMu_5yrkRNAa4Ksy1hTBPInFvikF3UXsw0VUeKHA18RR7PyRKySNTOBEvONuuCRMR9MH1o7fHsC-h7AUSQYRhTBOGzznyjY9eGx74U.mp4
295
- Copy of SnapInsta.to_AQMuFy8uLszNMm97MBTcu-0Eec2lfEJi0prqzmOm0NPDx9U9I7utp7ZMM-t0IMzeDcNEtOl4AkiYgdsCY_RjzO7E6ow_vSLnngvVXjg.mp4
296
- Copy of SnapInsta.to_AQMxiDEW8kAQhc0Mx0AFq5GmYPCJKWtHiX7KDgoTsW6fpsM3OFz-UJpokWUE4bKFqymVhCCz-a5CSxOa6v7nJNANYEXegfLOiU_5ylM.mp4
297
- Copy of SnapInsta.to_AQMZes238Mbmr3671Pa-URI2GR28P7r0OLNHXZD3QPChwzT-YdyedL7Z6U7xrUFSI6X45btaUzHqC2QKbKuOaUUx31SmQSZVR4aq59k.mp4
298
- Copy of SnapInsta.to_AQMVWnU24Ou4bCQcrbaOu6bKLpKD52qQ4pcOJ5xw7qnHCtHG7Luo9TFJ0lI1tXmXFSJ9tS9Il7iTSwaTZuR-JlExMmsKJOakZx_4Doo.mp4
299
- Copy of SnapInsta.to_AQMwl8pNE79BdvuP1kPzHP7Kt7jqAJu_aZLAoSPBjdr10cLGp0qiQArsKOeUZd4KxlRbZWh3wJvpWe7EO9Z760qeU75K-IVSVYYyCAc.mp4
300
- Copy of SnapInsta.to_AQMxRHgEfqIe-a6U8QH5HQ-cFHcMKX3TmH4MzsIPZ2qz2aLutWk6s30rMCe1bWjJ3Mj6_LgeHFnjUfsU20b1D7YrZeBmZFds4woBjtg.mp4
301
- Copy of SnapInsta.to_AQN7Z_Hu65ZFKpocBNIFZef-6Yao68UptqIbP8-Q9fcAN86-m1BjXeJG9cJ-uYIHUH4x-YrDP7JOxpN6KilEv5cGfvPUlKgniGEmnxg.mp4
302
- Copy of SnapInsta.to_AQN_Cd6xn1_a2MDXLaHb3Bnqw7Ka6puJ97hitypJ50mLWCfsI5FVf-epkOAHMmHpZX3X6GZR4WiuzYjGSLCuXG5cstaGHc8-HsM4uos.mp4
303
- Copy of SnapInsta.to_AQNcP6-msGaR1wMYJkBWOXyBbYZznbQpQdOU2XlftXoQgfBDZp8fJAfV_D7H7UpgYsDlgk1U0kX2xg_x11kOSiQACYDQ-VppG9Y92-g.mp4
304
- Copy of SnapInsta.to_AQNExyw3x1jxZjobL8WUGzOx0VV5st-aV66N3d0hZ6Uqu1We2yBHRwUCMYip7O9gI65xP65Epw5zLy9ILDUgHDf3h-85nvybaPpR0qg.mp4
305
- Copy of SnapInsta.to_AQN0OL_pAn8cKcSmPtVZ-TdXovUtHKKBrO5U1DLD17KCPHzwJzhiXJPf0fW1d2zPUenKPEnNHYmJQOLEVX4z4UDQRjQ2PxLY_ARfz_c.mp4
306
- Copy of SnapInsta.to_AQN1nlaI0vwJp523CQXNb2-6yUAN3Ou-MpFgVSiLV-8SffP34XifpIKSr_F4goN8JuyxCcZTqdjdpBvgxCRo8BSfOtyePGs1uDGKziI.mp4
307
- Copy of SnapInsta.to_AQNBvw6nsGYtglQUhqShLogCOBAjS27quKN93dcTpTVifQQC6_s_LEdOs2UuVq7ai1n4k7FX3jkhqm6My3tR8CZ0y9MdS-ao9qe4NDA.mp4
308
- Copy of SnapInsta.to_AQN8GyobLpXHCumpKdkcvjRTE846Nq8q7Ta4xONk2cPHSXuE1nmVn4wsdKzpFwMKoyqdHuesjCwJJD5bBfY7QBO3pLEA4c3lIzphJic.mp4
309
- Copy of SnapInsta.to_AQNbIE2coF_wcVOLSbfMxdfebsla2nI3kcGQJjAKZBQFgsETeWum8OgCBYsUMYhGOUb07UixAsorsoKVRw3bb5oO3aVme_X4GZMpmD0.mp4
310
- Copy of SnapInsta.to_AQNED8zeNUCZcqYEgG4Sc-Fme6GDzPB1N8x4kIaC2nH-94pvEvNYQDuTv7X0iy3Y7T2WqpzNI-qnzF8HYUXuzdGGSR5BIIqNrO7xC7E.mp4
311
- Copy of SnapInsta.to_AQNhPbKLdSj-mRE3J8R72_85qvM0Rst46DO5T3x3iAVAqxykabgjCVGV7PQQiEUD1qgMlNPTB0uEU5APJ5HyilNtkIdfqWctU2UEoy4.mp4
312
- Copy of SnapInsta.to_AQNl82Y4740Lg_r7iyvwNRJ5T7JehpgyNeEfbNbBldQgc9bfSG93R3Mq3FVrXgOYI-Az6bgwQPEDeuPvF521J3lEbTrcvVzsVtjDtco.mp4
313
- Copy of SnapInsta.to_AQNl-ZHsjXjqkTv7a1MFBhZu8Z9cdMOSvZP3MNzTnQmVgoZOYOe5XMJN4RGqSvYlwkEEDCbi2aBAsLZfumtdgcyvRxw9NIdCN84WdOk.mp4
314
- Copy of SnapInsta.to_AQNf47Y5eCg5tsOGO30JLFkplLxCNWvPmWLqWtP6omfMNEVH4qXm1-1QXt69m0mwHQaluJhmUVOOw8aWbOnRSTrQTqaFBGKpD3c0wt4.mp4
315
- Copy of SnapInsta.to_AQNgp_p_eirtIwjY5iS8Gz0TPJWWuzin_hvo6s5QZ6JrVkneABQhIfBTClJbeQfU8J7SUGYSaputzKINebBQ5eiXUY7LPbdX4s5rNyU.mp4
316
- Copy of SnapInsta.to_AQNhCFsYTfeqS6kSUig8MjiGicEw5Al0PYH4sbi9lB72RH1ELKIqfzrCyvLlcDdoNVNIjL8-Ztk8YdktoZTTI8NgSuwuWhnm0KX_re4.mp4
317
- Copy of SnapInsta.to_AQNf8LAq6ep6xLt6HMdNrLad7abc7TN6K-nHV_BK5UQsC8827Lg4jL74fNNfVWXF2pUOkaG8O6-GVleCMbyIDzIzWL6MmHXpJbf0ttM.mp4
318
- Copy of SnapInsta.to_AQNkceGq0o4sKr0VD03lPuFvKQHb6z-3CvX8Vu_jSrgIPYC_mg8Bc5dDhamdMKin1E-jNhmO1xRv7p3qknCKG7dYZAJLtmvcFCVdonE.mp4
319
- Copy of SnapInsta.to_AQNGfBrarF99za6ruCOd0tha3Yj4XwDlalMzLdU2d-jYXhqZBM-rDiHVohbM5-NrrJCZsk7Za73DPu_HMNhAzcgZcLwPSWwqdHneUeY.mp4
320
- Copy of SnapInsta.to_AQNGBYK3U9vJadMzzLxaTWRo9czwOONZ51L2qlIB_5519uWmCXEVutiXL7DywFUBY-fPOQf8rqXIxXcENF-I4ddKX0svmjIR-zZAaaQ.mp4
321
- Copy of SnapInsta.to_AQNp666dyGdpnnFEJFF7ajMYLe6dmsl4Zt_0-vdDTk4qveV4NS3qYPRsuQqSqNxuybnQB6lmuV-MszAQ6zj0hyU3NiOlYe0xhGZcQR4.mp4
322
- Copy of SnapInsta.to_AQNtHjyn37NEfjGkQki-eYz51guu3KvewGdL4nTgJxFTOlbi_I8NMtkF_dyicKlDxGvJQEDS5K-ZPV_0CpzIHr7CSh7b9ZbVFvZGe0U.mp4
323
- Copy of SnapInsta.to_AQNpX0rl6QiY5aKvpUT6IlBt8iJM4F0qt2G4Jj3C6O7WG2hm6dCsp6MLva42s0a3p4pxMweigX9zuRFYhyqVqZUyhq6pMZurCxedXs0.mp4
324
- Copy of SnapInsta.to_AQNR7WQRuuj-vtFojxOiBaViMzu16HjQDAhEKeTAPwE0WcGoK06oElzuNNdB89cVFUlIuB8BYFodyTPybiFq0AYbjVYtqxyCUtIonS8.mp4
325
- Copy of SnapInsta.to_AQNlKYm5xzZFIcgun1fpxPPich4iwgTMohBre9ClZ_0UEF5pB0nBG4rYx7UtzzeI8k1-ceGPPoPgRHo5r4fOdaFQR3jR0AP1SRQdFRw.mp4
326
- Copy of SnapInsta.to_AQNp5yMXVCDelzuUj1NW6souDLNt25DaGbDspPgrhRK8jpvlZ9FeQ7QC19GSwDs-CWHDsOvKB-qEArsSy71wSYWayt0C-zsE5EE0ZQY.mp4
327
- Copy of SnapInsta.to_AQNu0CeNNv0oWcIryByrvT7qAVBFBmGvfRjE157oSH0uRJl3lDV_K0Jh8eqJwyiilotvSH4PP53cn1TbVEpREQFktqEDCIcd9KGXnbA.mp4
328
- Copy of SnapInsta.to_AQNTT1-r_woyS4yTmHcF2R-ZOx3-sw8XtSWGvQL13II1OeHKY1MvrGpl5Us3YXkTc-QTUX4NyUvFn8TSK20xX-zgvioqg3ntMjZGgls.mp4
329
- Copy of SnapInsta.to_AQNQ_jtJZW35Nx8y40FfnPECqnqMoE0I6NqekTXFNYhVzRvt2mnS3QbJncscK-wcyNjiwGIOwCTFBiaH3wMamA3kp5y1me1aSgeTOsg.mp4
330
- Copy of SnapInsta.to_AQNSM2JayaZwqGXZQO14kkxHxfMdQVI_pki1ijFaHwU77SFZXB3PykNIaBtTTZJC_FLP924Nd5az2ESpEYZRg-W0JGVMCDfozQUA4DM.mp4
331
- Copy of SnapInsta.to_AQO-PsBtMC2PBXK2hCCepANlDhPqqoPebywj59hOq67N4zOWUiN1Y0HBBLP0H7QK5Y5MVMCAYkx0IU9Q7ZaX8RiqadV_B1jWVK2yRGg.mp4
332
- Copy of SnapInsta.to_AQNWdkXq8gvnURx6XquMHuMhylCIokXa5iBzhRQok975ocNelFO9sDFloTI_9zRVCi5g5NkYDmFNR-pXMid7JBHXBq8EnW1d_m8hSg0.mp4
333
- Copy of SnapInsta.to_AQOGGZXDD96tPOQg_9dl9V9IR9a58fXYjUa_PMRD5uJapLYx6FEEY5gi2KULqL95El_zmfQeNSVCOlUqcza6ToItJ9mRf_NxKC2enPw.mp4
334
- Copy of SnapInsta.to_AQO7-wXpI7K1mf3HvhAPL1LzIaAv-pzauo5A6259tZrHi_PrTkCMpWhThct0-sQYTyNiAkNwYP9CGPOf0hyhUL4pWoqS67utA2_HE1s.mp4
335
- Copy of SnapInsta.to_AQO7WX6LducJGk7omNuRpHoXTu2yq0sDx39G8-Qxc4qnABfNIo2vQJBizu3Vcjv5b_I5lY_KeURYOd_eOZgqWxjb1ZSXx3-pwjtmY4g.mp4
336
- Copy of SnapInsta.to_AQOFOPYU4D99Ee21PwFmmlFz9r0eHUYY8NBjh_wdxv0TEwkjEra1X6giwsqLJq00_SjQCNhSBUh64NYqFayB7977KndM6uPWco-xpsk.mp4
337
- Copy of SnapInsta.to_AQO0iYAfSSm_AlzF0UT2k_b-Tedt3MzzW-Hu5_NAW2Dgs3-21vIThTZ7P6XN6Wd8hILlSYjNpF2oROToMhCXDBjlT4MuWG_XlpGhvGo.mp4
338
- Copy of SnapInsta.to_AQOE2bF-mZH3pAl99XhqzxUfrCsa3OXUl-lG-5zQx5MtvcXX3ZEsWrQeCyu65oGHT47HAkifnA6zb1VbrHkTFSyNfFLUWwLqsKRKBSE.mp4
339
- Copy of SnapInsta.to_AQOf-vL4fMb9cIv9Fpq5WQPPLLoIFThg1ynJTpgH0KJgOJasQOY_RZnvLAXAcbYYkIxfwvKH5rerKBWsWtrVy7AhVy3wbmiN1xyvYec.mp4
340
- Copy of SnapInsta.to_AQNyuzKG2oLHVglJ-yS7pxqzYlfJKL2uVtjRycosiuV4n-lPUNGToa62V0hXG0JhqcruHvCS-AzoE89wuj2GNyGGsDeStv-C5T9HFQQ.mp4
341
- Copy of SnapInsta.to_AQOgLYjn57d1uYOHqpbccQA11Lyr9Knf5zpEOYDgN0gl1-VEUyMTczTxZCHvfPo3ciCFMb7ewyVf9m8ogKyEj4Lwy2FbEnD_qqPr-6o.mp4
342
- Copy of SnapInsta.to_AQOL6i9M1Y0Wsrh63VhPeKo_XI7khygFBagEg-MJp1PF6HLilDcdrRStgnWes2zhhepzY22v5uSkCDRcsLP-Huk_OdsUyRQtDSwVNdw.mp4
343
- Copy of SnapInsta.to_AQOkEdGRCBVGMh1rb23Z_NDjCL1Dyu150RWK-m9cLQCnq28EOOzuGX5cYFa3ABMZnyXtFGNo430z5eZV9cJSz3TWk31FEwDrgYLO5DY.mp4
344
- Copy of SnapInsta.to_AQOk7K2s3bNNCNmfzna_jNZKlKCF8Xn4jKFaRb1fr73KI0BGUaj_WaJVCJHuZjKS9boxVf2EiI6W8pQyaf_sexvVaMBG3Yq4Qd4A8SQ.mp4
345
- Copy of SnapInsta.to_AQOMEJzLVpCPdY0kXF8qyqvT6qO_8IyROk_mX_HFDUmQUehScVTkj1qTGHqmk-he7Qa-z-T6tLg9bxc8QW9X6lUKAhR9tWAA6Q126Zk.mp4
346
- Copy of SnapInsta.to_AQOolfQUZh5kT4acw4RKTorJcu6XrkZZDARGRK-w3BEHy-egP3ThPLqZNUn9ZstQ63C580aIsWw5tDmrtti1aJRchJdJIxzQMNAWvLg.mp4
347
- Copy of SnapInsta.to_AQOR5SYQZE5AiWTEDACdtRWpcFAERiolUws7JEjZmvVJcHzc7j1H9_E5SiVDsCrn-GBlaAcSpiwyG0AyNfRqJXwmAmJYHc1ayp52u2U.mp4
348
- Copy of SnapInsta.to_AQOlqUDnx6-TzUWt-eprTR79VxnhTsvJ8kXyUm9DOZbACQWtqk0JEOER7OL8am-oE7U0aRVDSSPyP2JvvZletbS4.mp4
349
- Copy of SnapInsta.to_AQORCTsQ5NmSBOLqVcNtsMsyT-gIV_wBAdjfGCgq9uf7TmVtMhPvjmbclDOipe2pLbv2JmHds47RAEno0mmk1iiQ_p5HZRu9neMicXY.mp4
350
- Copy of SnapInsta.to_AQOhMR1lftT5IuBVODqchYLpim8GQIb-oR4ioAkgrwHcEJJkugwtRylxKD0_XCkL_7fnVULqgxdP_p7cbvblojBlDEFNmBrj9QiNkyA.mp4
351
- Copy of SnapInsta.to_AQOt-PVeHQZp8paO7itoojdM78ttbagQpu9wpbVDNy9_-31Eq64o9mLIQaulGZmGRUfJv_teiHZq8Z7RavYYOBrXGqESi56FjHBm7rs.mp4
352
- Copy of SnapInsta.to_AQOv_iCmQhIBzPG2dEcxoIExgbNUl9gKWGd7RT8cR3qLBWphcfBbi2OYDaV6alAVrt9Iow3ci_4vC0MIBsApvFjZCAQw0ns6fh_GA2s.mp4
353
- Copy of SnapInsta.to_AQOTU1xiKrMrSwUX8l3s1NrtIzhOIQFg0hdaMU3WnqxaNuvdrEPjq8maHfP6KnLsOoAAjf0DzbyF0Q0UJhk9QZinZuPbjm6vmfZmRNs.mp4
354
- Copy of SnapInsta.to_AQOSKe3xq6WrCrQXUoH_uQN7_J0_WipbZ3GdUdJB7Sus46BmfrcNjkIvlu1IJhW2VuaHfOoh1niF6LQQ-DU0_KTLgGj6qepshVKdIYk.mp4
355
- Copy of SnapInsta.to_AQOspWd4aQEYDXgONwO0oN1WhLmUDSZXhATvIltON-ICgLHcCqgc4s9y74K-CGcbQC_waNsXPOuEkGKBbBG0waHNaJ_t7PNZB5rB0xA.mp4
356
- Copy of SnapInsta.to_AQOy_GFB6KOx3yH-FtgRLu0fGl27ePyYRXH_RvCjsgPde7qMGp9awIpo1WyRblupOlUrtLGIuqi73GI-idakktlm_yh68COguVMJgmw.mp4
357
- Copy of SnapInsta.to_AQOyz-mkgigIXQeVC9K2sI64pkhnLpQgEgyv44c6r61AM7TDeNbSqF6cVXvJ8mT8gDD6v4fYSJvBGRSrXf5tbiW8GzcVvspr5OzJcmc.mp4
358
- Copy of SnapInsta.to_AQOu3F-Ir1ozVJdY9DExI3OqpNE-vaxhclme_lU22kFrdSQeqLz02L_dEMBAh3fJ7NFa8L4_62bmyRrnRc1DflSNQhrHOU94IQokCVU.mp4
359
- Copy of SnapInsta.to_AQOsow3NzBuDArcpGIYj8wkAYqiyYR_A0Mh4flRXRDIAkadLV4YT4G3JuvOxNNC6fvNOaYW2CUC6WKYxt8X3hApB_OlEOx79cEy5ysM.mp4
360
- Copy of SnapInsta.to_AQOWkszQ70n8Sqo1YR9CdyUknHOZsbk2QLNHWLpG6yjTfe9LNHc_feTjMuuh-j88vZLTtggtpS1NtdhnwTF3zNDHFIFp9BSKPara950.mp4
361
- Copy of SnapInsta.to_AQOZeJcOxUap6NpBwpM2eVkhFjlEN2LTXlIc0RGDUj7CG7sSGUYNFiW5vUjw-Mlr4o4AIqW33sZOCJhuPFIiRuoRIPArd1dwU-5uaqQ.mp4
362
- Copy of SnapInsta.to_AQP1T1CT75HUGz0xuFXU6YF6WYWI6TFlweSxPmncXQXLj_kK6M3Nzyb7QAZUvRbcDSBIkUwChRKljB2aDZRVWAYOc9ZailQvsymo_LM.mp4
363
- Copy of SnapInsta.to_AQP49KhE7Wwr6fcqLeES0N1Tl-rR-KgHdEngf16DS_Dppc_dxD_3OSenk7Pi22dv37RH3FekJK3TJ_of1NkTCQgw_-VvFUEU7ymulPU.mp4
364
- Copy of SnapInsta.to_AQP111ZcMGfzhBWiY1J4CxdiawXuQz7KlLJZcWwSGQVzU-B6n6GotRhMdX1h7hdwz5W4xkXJA7FbDsXOYXpzSwcC.mp4
365
- Copy of SnapInsta.to_AQPBMyqBgikkFZd_WyR5yCWev4vRm_O0MFnL9EH_lFEwTIL69pL0eXh0KbknsMTFPKcqi8F5X51OMaG2LCEbH6z92wc5uk5qxRGDTUg.mp4
366
- Copy of SnapInsta.to_AQPB4oeqXlRL9svYOO-ueuqcaUTRqNM12Ay4evGYKGAVA5yebeTrTfxzbYfw5fw6i-Wc31u1mNIuc7Yv2o7nIgdqTknNN4OXD9NrlqM.mp4
367
- Copy of SnapInsta.to_AQPdf8mJYZlKpNHTGJ_tLWil5jWhR9f3NnRkeObLG4dVcUwJhTnJT2Jhh-d2_FYpb_ZcMg7hpXvtyojM50mSN11jCGuGf9XDKRf1KZ4.mp4
368
- Copy of SnapInsta.to_AQOzNzPsK9WL-0mp_qgdPMpMSAGJraUHkEOudrVIwQSqaqeXn90E7PuHWa4J7ir0-irPD8Kz0bkV5hHEYRp_aqoz232d70pJ93FuvKs.mp4
369
- Copy of SnapInsta.to_AQPAvoFEeRD8T_eW4twwXJQvWJN_Grpwew388rzJFibQjR7sDZXQT-Kgt4PUtrunMzY9EyY5IngBbgXZdRAvfViOjfRYJrAL2YTzOkc.mp4
370
- Copy of SnapInsta.to_AQOz-szdnsGQSOjGMlZho5AmXq6L7xykDpBpBa_EnH5jYEJAjIaaghnlJToWfa5swYJQNprp7NCVo1WlkuTzmgDFj_a2noacBxFhnPc.mp4
371
- Copy of SnapInsta.to_AQPJcavX7dbv6rfH2ExncIuxidIvrAC_uYaRAXS1JP09tyV52nUAkN-pPwtWIaWbt_vTlfKKxoiXQaA5Mffrym4Q.mp4
372
- Copy of SnapInsta.to_AQPNHK63ahRvTGywTQ3n6SLGC4L_VOmFVmaCj8-e8iU4HW129SbFhUfZJthwmAMOeBWYkFHP8o62b7WTbU5_-mqW3tzjN0sXUEb1n14.mp4
373
- Copy of SnapInsta.to_AQPh43idkTQwQxR6AJoMFneAZa_i5OkhRNJ066fZRzZNMR7C-qNV04OSTgnjBzFZVGu3B26JHMvPPyX42Tdze4MhXhOeU1fFOZMR96A.mp4
374
- Copy of SnapInsta.to_AQPgisSvI0cpJUuxxZgPMklz-tljYuSAPk9xcSydgKesWUCFhfdwNVhFwDliO6QaRthJmBg1AFQyPA2o5Svg6ti3iNrOQB_8UMlJiKM.mp4
375
- Copy of SnapInsta.to_AQPjP5ejS0zI-NUBE5162mxrVkOJRzpmKTyIJu44elIrvrwYd3qdhyPNq7qJwHOmBZg4MuODk-8DWtnz0DXaHeVy26P6zfsVtN_EyRQ.mp4
376
- Copy of SnapInsta.to_AQPNprrmv-GKOpEAbZS36B_2BJOsavF9yOY0R-O56ChC5u80tLmUT3rX_0KA54JpvuFkdwRaBLDallQaEwESO_gMkCoAgO0Oh-JjWpQ.mp4
377
- Copy of SnapInsta.to_AQPfo8-IOuBr_WmkuKHpKmg5dzTuzDjyjUMeM7Gsi3SRynd59FN58nW5BzUa2pVysuGqzaIv-A5d6AemIWNfJ6K6FjqFFTrU2yhvieI.mp4
378
- Copy of SnapInsta.to_AQPhcwlpS0MCNdwfYmU9mjbrjj1rdbJQ8vKmVlSzAkCsY6DaMT4SGVKo9XUc6Ce4eZzrEsd0Wc42xM7ztf1jrFZAHs7v-zVA6Wy9laE.mp4
379
- Copy of SnapInsta.to_AQPf3Cj6PWVoHIZpkLqKzWwyZnFyIC4lnx_U3vM5L17XLPeEhHCr9JFGCHMI7BDQTLLlTZG0ZJaznRyHcR3lZvhRMAaLu23axSkmRaY.mp4
380
- Copy of SnapInsta.to_AQPGlIt6VyF8bmPaQRNsC1PA33wBmwqlstXeKk9OsjmAdVng1AwyViAsSZOjNTeln8PxSmyjX33koIyK6vFcMfkaSkKIxK2YnKnnJYI.mp4
381
- Copy of SnapInsta.to_AQPysWY2Fi_klGNf-PF6LBMUq3Hpd99C5jQKqVgMfgWOuC6HExjeHjcZr6Lf_yN6irbMyQOEkVgf8fZkZBmI-LSrLpARIzMzDpbH13Q.mp4
382
- Copy of SnapInsta.to_AQPxVBrzmBDrUfwACLs1RXZ58gEqfXYX_292-GOeUf1KN0oJ4A55Ps5__OASy8-F1yd66OyOaEIdcHdRv5zKe68NO97fjRSM2bV38xQ.mp4
383
- Copy of SnapInsta.to_AQPzze3-pBPR7iwd0SwmtXIkNcPJJtgD6JzOFJQ8IqsPRIEnNHZNT3USOpdgkG7enehwCoA40NV6SRbA-RmOAM8k21gdV7HFMxylBw0.mp4
384
- Copy of SnapInsta.to_AQPxJc68Pg2B0UEptDIftzAvINe7nBfhy8kmzsStiJrm9uQbcEo11Xitq8Osd7yejspyii5M6MU4VfibsjitJY4aDgpgdvLWFghzMC4.mp4
385
- Copy of SnapInsta.to_AQPQVYfGHk-FuVhc5qAkmBldfGDliwAP-zLgrQbF3wnPP8UquQ7A5fpY89YJO4JtE7jA4yJcZZVAml4PXUlCf0g8HRwJ9LNohiVARZc.mp4
386
- Copy of SnapInsta.to_AQPS52QGcJ-gvQT-vh8ZY0c7XwvDLo5hJvC_au9PjHFMC9WIomdK-3icG1rRLJQ7Z-QMuO5krpomkpEuWcQN1SQjgMhsWkk2KgZiJZY.mp4
387
- Copy of SnapInsta.to_AQPTOaDzi57cH64K0QFXBFsTtwujHQ1mAe4cQLJ-f_RWYo6evDUKEbSBNLrM-9oKpDZlLOD0c0ie8gOEmb9hIhy61WQNaa7x76gEoOM.mp4
388
- Copy of SnapInsta.to_AQPyN6onaiIOGTOGvnxxgRVhTCPXJMzOPQOB40H6VzD_rkZli3fU6gHQWJ-Xrzp2la7vkce0C1x1fm5ptAGbuC0cLECMVNi_Zv4nmmE.mp4
389
- Copy of SnapInsta.to_AQPw7EDS2GMRJmF1vDPnWu06wiePb1ttWaC4ZCb4tbLDUNsOrfD-UtoIiOUdDDABbvLTwrCXNwMZwJBMFXQM2cg9QLJMbmr4UZ-zDDE.mp4
390
- 1.mp4
391
- Copy of SnapInsta.to_AQM0AV8P5J4KHWNGtQTZJKW2_C4wjhAmJmN34wm0C3FaU6zuUcLYq03U66F3bHqf1gerH2dthxTFvhn-rHr8XOcKFHg4vSWaQdQyuuo.mp4
392
- Copy of SnapInsta.to_AQMbUdclLkquwn4ij_A2AMrv5qkrrYj_UGbYYy2f1-K9HWnZsgdswEX81z7dhzMAZd0lj3SLBPuxbLpOtS4EhcybMDKLwZbC9WRZZT4.mp4
393
- Copy of SnapInsta.to_AQMCuEbwvHtUeWjmChPeuhzz1Y3urXT3hFIs-Mb3Dfe5phUzHWyIAgeRZzKzwAZcUK95Pt-wHBytKa4Ztc33PgOlevA2_bltvFXEoFk.mp4
394
- Copy of SnapInsta.to_AQM30cP8v7GVl__0ZTd1FmgnU4WpWz7uwVouQtcW1ztqO1N-EzKc06FSQPTd1m42OcvtJGw6i0le3trw1DvP3JFP.mp4
395
- Copy of SnapInsta.to_AQM2Zf_eITyJUdcyUuQDefdg_ZTJ5OJqaKQFeaBbDw5QAKNlfxb3HDQQjTW96L_9WUoTpMAulwVgci_arX5_06H-X9ZiJ1rHq8oEruw.mp4
396
- Copy of SnapInsta.to_AQM-taWG00NndZfFA6tiIIvruXWK0usqBdLa3K4mdGfkBnXIe2N-CR2G2ZIvmh3Qsn-XlHmBxmvEknSBzy_aJQ5QvkcPP47DhM8juoA.mp4
397
- Copy of SnapInsta.to_AQMDehxo8MbpUMHcVdNJre36crGRgIBg_tTBvQeJdKUFi1Hk6_BOiJTtTzJEqjg0xET9SX2jQfiFqqQxSG7Uw1gngtyX3bPFcXU8KvU.mp4
398
- Copy of SnapInsta.to_AQMcAe-ZHYy2ShYlOcr_0No40moUJoQIh6UwXzhCjl543GHSuRZ2Je3rP6MpPMyaB2KfLaQLxSWrVD5QM0QK0u40rl2aK7VesNVR4VQ.mp4
399
- Copy of SnapInsta.to_AQMapIwB1pIZeDQdAX2vqvDRUJ5-NVUnBzO_CedUnIhILLNdP2GABJi4_Ysc9zjfrCbg2pb-XjFlOz5POp0KVuCEeGLcouqFsY_6CUs.mp4
400
- Copy of SnapInsta.to_AQM_OBI46sSkn5EG1jkqOB-G8DfYFY9fIPnS7_dwzvUbFpM5llVobirWXS0xbpYFwoj5Q06ySa6135Xje0WciKfvacWtqd5ffq0i-PU.mp4
401
- Copy of SnapInsta.to_AQMqMdPDWFb5kBxSUepVxwJPjjyUNQkkuLquChnl6a5T_b9-bjKoYAP891O24-i0oyBgOQjIMbbGwAcaxnoDrhc7yyN_ZTn8elJOq7w.mp4
402
- Copy of SnapInsta.to_AQMHJAnu241vzf2zoU-DZuUjoDIJoOcsfBIl_D9GruAtyqGC3LprvCtyslZ_PilOrvZ2S5zjwCBVnaJl0SWb-6V0v135xXXH0rh7xPU.mp4
403
- Copy of SnapInsta.to_AQMlz2zyLiIKCwJU1NVA_xgPdNndjtwkv8rGV45sBsJ5pnsKpH5uFIxwhf99zN5Gm5Rg4lw0AvgZobnwfO5yM9SPDiXoc8qg7dGWmYA.mp4
404
- Copy of SnapInsta.to_AQMgGQvzv2yM4PfM1YXCQzypn0x1VZmuOMdIN8657gVeQAVU_ZjP6rHutlcsCdpNs_THn2035i4mDF5hwPKD58vg1QYGgNKpe3wtXoo.mp4
405
- Copy of SnapInsta.to_AQMgVWgRvsfDGKRFakfly2zluZgYwHU1lxpSGvM_2mDUVZC7MjWOdIAiOydcvEjyRMdjOOaesFfuGf0_Mpm9hUg9sr59ICoLU1Sjkm8.mp4
406
- Copy of SnapInsta.to_AQMfVEZvu6il6mZFOPH3dPAJiuIDvFJFbY8taFNbTreiOh0UvG-7EOmPk1JBy9IBCEYMpzyjbp8frwmo1eIpYA3kSr_ngKPFRaqrLa4.mp4
407
- Copy of SnapInsta.to_AQMjlYNzgCZC3vL3Cg1AvvNPCTjHh6VCUu_AMgTP5GCiFIv-svvLWplivwVXP5SG0lq6DXFHVhtex6Mp2T4McgH4.mp4
408
- Copy of SnapInsta.to_AQMngwNgQ4-uydt47KTKwvYl_ITu8XZLEk2sHFkM7PVPitSQ6P04-stZ0Iqvb47Y_HXDINmemv11Fi07goPr-h1S9Z-MiwhVkQDKJfA.mp4
409
- Copy of SnapInsta.to_AQMfMBpd2L9Z0Czg3YDf7kh7-I8dW_UWXEMutML6dqQ7wMip9-oKjU0J_lTMyRknQK-qj_fQiHVDIMsGf3gpUb_h86BCNrAmtleEic4.mp4
410
- Copy of SnapInsta.to_AQMDJPqzvNt6ieNY2f0PeRf-tHN61JBNkriivpUi5WZPeTkeSoqGNEk9jHzCjgrFCI_cUUDh89eNcnujas3EWYhCbkk_MC8Qx3-SB-Y.mp4
411
- Copy of SnapInsta.to_AQMTVnkomBHRom8d66KxbavAmkQi6W974GHy2U_zo0cf_PNe0bV4RrC819qn0BIfAYcClOCN5cKz3FIHoXfK4KRGKCWOhWz9hebr11Q.mp4
412
- Copy of SnapInsta.to_AQMRdknj78zhisXU1tZ_NiwJi7t9B-Po4HxFrA2MJtKPuZeuz_GvuNrSKs97kuZnhzTXyFV6Np5QV9XOfUhBr5ksdotwi0lLog3sf20.mp4
413
- Copy of SnapInsta.to_AQMRHOIHRUc8ScKfu9MYMgizoedO3oD6eI7_5p21z_YAZ-RoxheXk95_-ZvLXGFvkoCiYseVvFIXO4heUKpuyYHfxmP_3PfeuDiLUH0.mp4
414
- Copy of SnapInsta.to_AQMzYce0rawQdYYpxUBILMl5qhZ0B587IMAIpMde7kUL9j1DxHpofpmu-DRotI7_tgzLae01JrX40G6pr3QHW9V2G7Lmp0wUiKj1rkM.mp4
415
- Copy of SnapInsta.to_AQMZDgdOPcRVexYVu_nN4beHw25I6kPmae7uSOOksfNjmlh_AvST7GAPXpplscK97R_oaX_A2VK26vNEMYQp3H6rxhavYQ1nWo8bzg0.mp4
416
- Copy of SnapInsta.to_AQMUm0dWsXHc3kPxUUAV9ZXirqrvPXX6y3mWCbNZNafcMb1L9YyyrUoHAhxs02Z8_1GEQ6rxAHazBp1Rjy-Lnhr9tXLZR2CV-XN5W6g.mp4
417
- Copy of SnapInsta.to_AQN-ST2ETVj0MTxPq-nlA5Zb1FLhv9YE6Og67yBR5tRsCUMXw8QMCScsMnd9wicAG9bQ_Q1aBqQuHklqUc8s9wChYt4eXL2wMXZQ_Nk.mp4
418
- Copy of SnapInsta.to_AQMZ1fjIu8HoK65HVR3OSFl98GUvdc1853q5Gjv3Jv20fgpC2E6A_WWkktDEs8MTcdOau5yqzw262gsGz3rY_w3CG28U1xccvOgRbaw.mp4
419
- Copy of SnapInsta.to_AQMUBWa9p1NUdN816ggFrT7Ho1-mg_i6qhPfierRWZRPzpV-GfdnXtHdKxNwoCVlkVsUVQzyrmvqMpq1fK0OlC_i3L5UHRLLmxIjnww.mp4
420
- Copy of SnapInsta.to_AQMz1j3RiW38iml_uOBpuUFzEUbgvN3q1kDA2MPDnZ7xZeaq6OFi808uDmK30ud27sgNbTOwMyA9H5t-G39dEPrAsgSwM96cwcE7M3s.mp4
421
- Copy of SnapInsta.to_AQN6KMBKWKEvNMXgSg_mR37nQ0fFgXlLl6foAPaiSm-gyzYr1B0YI-YQxHDbI_NIorEcE9BQBBl8ErteZc2GK69jH7VDnez0oAjD05k.mp4
422
- Copy of SnapInsta.to_AQNBKEngxB2f9cQc09aGfXxc3F5dxUql6W9e8IGjSljSHu_roCg_oAxdktm3TmRIoRiP7S4B-qtdXFJzzRXyao96Gj1DgthQHtfUpnc.mp4
423
- Copy of SnapInsta.to_AQNAoTFxyRjF7TFNSXiFYoJC20AdiKWzPDe7XHN9uEQPWAO_ka2Qhqh9_xaPVNKGDq047Hm6tP4lFgbKCmlT9wIIVxRrspiW3f_WUO8.mp4
424
- Copy of SnapInsta.to_AQNATjzghdgmmVpB4rh-_704ZonnB8oJVTjRiNOQ-WiKsAmikhmT9orgm8XHHBz14G-sSmIp4DCOLyQyBXqQcIvMXV_EWZhNPqtlM7o.mp4
425
- Copy of SnapInsta.to_AQNebO7zF3d5kXPW1sQ7H0xFkEMRGmRTECqzv_fcmtCqfbPdhaG0Ev8pzitJ1DI5zDYQXFHi_dzpjFnGuv6HCpFQzmKsC-TU3RI3Rl8.mp4
426
- Copy of SnapInsta.to_AQNaGS-0CfEDIY_c5z0DkSn9_aHfzLCkuZCNAhQ8tAS9nyoFVRZZCFbcAl6QH_m20rIvuyrm2QK0BgGo7Lv-CDZ0sueZWKlcluzpGHQ.mp4
427
- Copy of SnapInsta.to_AQNb9RaM193mu_9zymMeJD2XWoqhJ-IkMk1ySPikT6GufMAWcHAHeNc53UudMEgULYAL2Xbsj1BU0y-hkB9WL4abYyx85GbOgcFUysQ.mp4
428
- Copy of SnapInsta.to_AQN75V5zt-xZecmPMbYmFN6QsNJrpiL-AvBGX3qLrvfoliLzlJ8n61CpQAiEt7o6Bn0ysYNs3g18fdMLTI4Dk23o5-OvHLD7LM5PqiQ.mp4
429
- Copy of SnapInsta.to_AQN9y3DSgXfgSe4aiOusldpgCdV3L6xNnbMj7qULmDQAkZgzuULaXIq0PV5KAroAujZ_EtncZGS7m0D238vi48uqpWM4QzKX8qhkC_0.mp4
430
- Copy of SnapInsta.to_AQN-vFLghH2BswEyhZUfit0tIJLq1qGKc4Iyfim73wXE7tF435IyEIXvOJqTJqY4eFyQpEAAqvW41FLwFcGPxqaDrXXzHYeGy3_UM9A.mp4
431
- Copy of SnapInsta.to_AQNI9FZuQU6IcHrWirnODkeOtDm33SV5QhzIsQhKfChVWNS_AxihImp5kRseiQ54ZfNhfn9jyg-JASoWNwNi4_U7diFlojlfE8WaoyI.mp4
432
- Copy of SnapInsta.to_AQNjA3e1-jmKp4_kpkZIijrJp5BGP6-xan9E4oqn6BIETw0CaHftisMBTwA-bz3UmJ88nW0PPbrkna03LGQQLedtpJGwbhBtYG7pBIw.mp4
433
- Copy of SnapInsta.to_AQNklKv9fh-G2DamINITsMltSBHj4TGJkgxMxBrefHNarmj3tEm2dF-LTN3WgeCekgjlXktd6nJZ8BPdTX_hOBsKIYgKONvDGN5IhhQ.mp4
434
- Copy of SnapInsta.to_AQNhszwxAZFaLU5ZPBjkikBIXmcmUc7fbSvIAY5so-eP5xDpKRSZfg3ocXuBv7JiDMd9npcLQST1ZmyNweaxpWOC32Pq1SdPcVF2uvQ.mp4
435
- Copy of SnapInsta.to_AQNfRHElJMeWR3NCpEutWe7eRZXspB9lzHVpTHtqpwFlfVl5Tzu0l599bCPVKuqquwiyx2QKY0QLP7nm4bCgAYGoS7eRdiWfE4p0K1Y.mp4
436
- Copy of SnapInsta.to_AQNNrpM_f4WMIGGK5PccaRRZKtend2dutU_WDm6FCiuqfzkbU-fesLLWDEzAizZYDsldraeI1lRmM4e-cHVSxYcZkcGQfuW_uo140GE.mp4
437
- Copy of SnapInsta.to_AQNLvkR0XC2aSp_WWld8vdKQvOUCShtlKherRkk6gSDkINX6sM3j0ui0ASRxLevVODWJ17JXz5_jiiUN-O1DoRmmVSmi8dRALBDqhps.mp4
438
- Copy of SnapInsta.to_AQNKjl9amWtGWMu22rI538gDPYni0hNNJZN6oAfu6VUdKVWjM9w1VU-IqvXWswuGkQqfC8ylhPDSaE0wcCdKwVFpvvDo7zyXOeZxEUs.mp4
439
- Copy of SnapInsta.to_AQNislmbBbLhlXEUp6th-BUKgdURLVDvO1P8y_65z6DBEX80wLp4Dwlv-3-WaMH1aLuXXWoEpbmua-FlfUJHZzPYOp7thuwHIL0GTVI.mp4
440
- Copy of SnapInsta.to_AQNo2C_RqzGbx7dwJcqT_oBtfWbO4PhwnLD7oV6ScGahuf_7CPRnI19pzHQoHp6yrZlDsM3UXptRvFn-EiMzyVv6mVUXCYU2S2ljXnE.mp4
441
- Copy of SnapInsta.to_AQNXoUg4cG8kmdeuL0gD-Ycqoe0MrWvK08-ZuloXkSTcW3KerdnO22N6RYyQhaHhgkSagD9GeQqFVDgMssKBYlcIRYMYnaNlJl5Uq4I.mp4
442
- Copy of SnapInsta.to_AQNqUL5ZJJeNFXONj8O2nx4jUxo5r6hwAx0xtYmwt6HGbvSEVLgUtevPZQMvO6f2JxQ_K_d58t3wXnLMVOtTH05AYMqboOSdbg72EjI.mp4
443
- Copy of SnapInsta.to_AQNz8vpVK4DJBt-N6V0Jp4cTgq0DOebjnRpqhTNSBeEGKqtCT00U8lY6rioAlPEpcRKk50Kye7dH2XpQtp5SUHvE.mp4
444
- Copy of SnapInsta.to_AQNs8mZelvMu0m2yDKCFN3Qu-TOriexj6b2A2P3_n3ckTgb8eEhgKdqpOCRdKo60a9YRYjvXWiGP2plvoVVIvzzQsdqsP426JTR6HQ8.mp4
445
- Copy of SnapInsta.to_AQNT14sJxGf7aQwUKqKO61kwBuxexewm9Nx7RHwdArE2AGGqOq0Nyk5K12TjchF7YJ6RX7B8JIEVb5Dyy3Y8ulapfzgFf_o5sKThICw.mp4
446
- Copy of SnapInsta.to_AQNSDUbMuxdO6XIjtDgowAZ74kuzyxaQwqHCJAATj4h0qmE-N6oQq8J9KeKjJk7DPQ1FU_WKTuBi5eoTG3mX_bWy.mp4
447
- Copy of SnapInsta.to_AQNoqGnsb3k1CTChoI_DAMy-_KFpQUfxzivkDvinnIZmiH_VyEWjdx4Ljb9AMhvVXPhmsoO4YKoTWCiYdMVwOqIZ1nA8lNjhd2lxQBc.mp4
448
- Copy of SnapInsta.to_AQNZu9ALhfGvlYTJhM_H_Cxhy5ahaxGAy7lTtEbUi3I-TyxAXebRosSvCDO1K4Z-GU-zuHPw5BtdWmkunvW5s-DYOFsMUsZiX68rQ6M.mp4
449
- Copy of SnapInsta.to_AQNZpFaNKZbOTlXpTw9F8bQZV2cCO2EHzroMawHebjZio4Lhu5l31phBASNEo9Vt_FiGPk74eoole5UgKPxw2K6zWtkDhOj8nKoV67A.mp4
450
- Copy of SnapInsta.to_AQO_ZcyvpzkpkYEyY2u1mih84TV2-b5ZVN_lAoMm2bheRUUKhin676PnhqEkjQHJuMk3GUJ9xqU-weKT5nTZf0xC22tnoh-eVnXCUWY.mp4
451
- Copy of SnapInsta.to_AQO0YvmPetdVa4xBYsilUdLier3u19i4Fhj-Lm82fvyy5EbOrDi0FHccwwQ4DBxVhCp8KDQpkpzVOa2xr5ErIIb2twbjMyNW1TDvrbA.mp4
452
- Copy of SnapInsta.to_AQO6xBGRmU8ew5pKF1O_pHcoGYH-MM4A51OIW24UHK4biiVdokf57Y411R-W6YvmbKXjgyzTWKCeRkQniW8--o6htUANSvu7veXTnvQ.mp4
453
- Copy of SnapInsta.to_AQO4c-AR6EA9Q_fFyMLakB82nkwavBPlYo5fgR1xbuQuFbRaTAcJGaRmKs6uo3oalBYxlIll3aUQwPGfHn35dCggLXG3Mw99b4m_ntc.mp4
454
- Copy of SnapInsta.to_AQO9G2JMub4QMQxUe3PhN3FHcSlNF84s2yUprbGOXoBfR4EuVenVQcRiJOEs13YKkX4F_xqG81bndLelfTlq-HVIInt0KChHQmlmacc.mp4
455
- Copy of SnapInsta.to_AQO0cb26MOvBHXJR2elHaSV6lE0FqscsSf1jLeVnzL2IYw0AdRRN328F8k4xJyiryAkMn6gJcUpNBwxraUdHthPRse4NP_0p3NfaFWg.mp4
456
- Copy of SnapInsta.to_AQO7M4qk00x1hdbwmYjYNtJOoYPipIlq1_u5EO4upOOOVsKouSXsk8UuH6t4EoZKbqZSHaUNzJJAC9fDy4SalGjrhrIuUpCHDCdZIHc.mp4
457
- Copy of SnapInsta.to_AQO2_69-HvaItQbgeaRkOwCtw7KvX454BmRLJVXWM2m2c0ygSfVkFRpdGaoDW_KK-wdKr6r5B6wE5smoX8SpUgDvsCb0LBZnSOl4U3k.mp4
458
- Copy of SnapInsta.to_AQO-coSM5Qu-3kAXH-LUFtLhWfVO7E8-drms0y3gL-JmEyFWs3hdI3Lw4YuUXNZLEn490T62iaRC7rVqUbjVhW8YyiTRxGdpI_f5EN0.mp4
459
- Copy of SnapInsta.to_AQObFFIYf4nzHdZ6-4Bzv6nYbWm_f6bh5xwfdHevrfSk6M9-hz1DJZHBYIF4j9if04CrgifV0zAuEB8xYtrgxFMtTOcu8Op6ru4DtII.mp4
460
- Copy of SnapInsta.to_AQOIUlMdKYw61Cco25ZKEgA40U4Wpzg2mbgkL0nLjFrkK8AZEQA1vgpCvhSoL1KjovumItwwn_IeKeW00aSsw9Rol9IHwFQUDnGuUYo.mp4
461
- Copy of SnapInsta.to_AQOGKlViVMU2cZV7RaruGOf1BChGgc5TEoARzLXoPAGTdw-ShpM1iwA7nbJRZE3V8HAeKoVub9mEoLNGwhSbKNyxV_1fWbEDb9Sxppk.mp4
462
- Copy of SnapInsta.to_AQOGbqfilykKBzWZS_MFJw6nwSttzkURAXVCwj0uHBcbO8ZLQge8-NdhTmQZS4b9waw-opg7PZECRU9eYQ8zDFG99NhyJlC0bc4DtmI.mp4
463
- Copy of SnapInsta.to_AQOChIToy0bqg2Gp1aMgqhMClAvfltI-dZLstsIDlAQ1ONEIK3YnRjp_Jo-r82Thd9Ej4saotjR0eOLNTkBVIAF6Ta-VJV8lmJTzyU0.mp4
464
- Copy of SnapInsta.to_AQOhT3750n7SltQrZFLryaDoy3ZJa4vdAs6Rh4FAr0omrjUBSiBU3wk2DdRFHg8I6FG-B5MvFxFBLVmPdnE3hwSCHLE-74tibK4Pz8w.mp4
465
- Copy of SnapInsta.to_AQOJu1Vln4pipaEC8KGynta4CDJKyLhXc9ncX1qGg_ewNvLgEvg7lTfSVJQpDhTwFbd6AQSZXCZ_4bd0gNn6K8PB3U5Xuq45VGvSOWI.mp4
466
- Copy of SnapInsta.to_AQOILWpmmXQEOfiEvYb9HjQaKEoJD-pVWp_6sqwUqY51dQWe44aj0mH_fVHzNk5f5W9xXWRRQtxPPXA7SsrQ6SBrsyiRXqvYkGEYBdw.mp4
467
- Copy of SnapInsta.to_AQOhUs-2_bpEHnOLxPX19Kf56WQfyemWOqeULAWHQucNf_oW8Z0a2fdxReqzhOrfMfrz8knSyrnr64fsmblfQ2tGeAaupFRYjYy0POs.mp4
468
- Copy of SnapInsta.to_AQODc7HdwKEaslKpNNxj3tPv4nywCKuzU4-21NJCg2ExpCgnIzL-t6cpI3TIKquDbYNwLL49v7DD2TjjDp4Wncnt9uv2M0HTwjJPHmU.mp4
469
- Copy of SnapInsta.to_AQOiNMiNd93YZOFCkkdHpll6E4-5FIbbyKyycsLNq8WP6zkH9nhqD_yst7HFCwn27M0LaQx_KsrrG91QyUl4N0-GUZZlQE51Is-LjE0.mp4
470
- Copy of SnapInsta.to_AQOQhBrh8oONSQ4Ph6IKoweNYLPO201wibOMB8M2JK3aa8LHiqqDQm44uOr1ptqf6RDj--eRssHMwivgAYqrJ5uefQPsBQ1EozgLWlM.mp4
471
- Copy of SnapInsta.to_AQOkleawFlGz-DKunnOKHeKvJQ4Ve67qm5CKu4_IgnEmeNeblKOCx0MJabNdcL5tc7OWBKw7-kKoAfgRUXyy85of3-Q5fz4rNjnwxvg.mp4
472
- Copy of SnapInsta.to_AQOroW8Vq-2ondJDA2kxh_jh-dr23clceTEXSA6i73einTnk0G-jrImo5vfkDljQ8CnT1IuOjhCcDM_jmg2fRNM_00rF3B93jHiZ48c.mp4
473
- Copy of SnapInsta.to_AQOtYNvu9zo5thrTWvX-sxwpioUNkx_u_-kgyrhxdSyZ3JM7pgbLkFxbPdF1-xCOUAD4WodY0RykoW2zIgKTrGqwDV_WP9BEA_qx1d8.mp4
474
- Copy of SnapInsta.to_AQOLTsvMiFu8x0DkW2miNUqFpfSh_Gt_6lqpk__qtgFCT62Xg3-zi2VANgbqfBr1-DCSnK_fEd7l6aD0dKYKvwQl3tgAxPHJNnSmCBI.mp4
475
- Copy of SnapInsta.to_AQOJZuWderPwxXEro9pFNeRAoLCtmEs2_HpDkOM0zsuARLEJqWbcOblWVq-1tpG1LTHTjmUYZckvqnruLdu1McC9.mp4
476
- Copy of SnapInsta.to_AQOKxI-cLYEN4oZ4HKkxqoY2bJ3DEP1UaKtEhI978Rf5vhPSxOjXA0MVxBDKTEckLBcmOgosVEffzbeqiVi-bHJlfl2r-dRtt_j1Iss.mp4
477
- Copy of SnapInsta.to_AQOMQFGK08nY4HXiZU3nVrnkdOAcGQyjJBnrmouPj6uUQL8_AeR801Kwy-o8I4boPLYSTGS1S3Mz7iVjz9FP8YoY6Va1j4jHTNwsD8U.mp4
478
- Copy of SnapInsta.to_AQOnkT7UqBYzkxguFisv4vdDnHgshvmYEiPV4mgZamIOMOw3zAGMzGMWbFNJR7fDHSgOD7BKp9_8HMzeS85Gmfaz.mp4
479
- Copy of SnapInsta.to_AQOn8T9kyKRc2o0ZRH2Oxs9R5giBdbGwsMpZ9DZN0k1PBKZqwgIRke1-C9BXlY-5rM_vXclqhyF5m58segaSvXN5ryuujAFW3dnzBXQ.mp4
480
- Copy of SnapInsta.to_AQOv-RAseW6ljEqvz6OaI0ylSfymid3w60DmwE07X3ohw60JrW1SdgFjfTRgnqga900PuMOv7fZDQ8LliiIjHv2vchUSGr_PVMRjyjo.mp4
481
- Copy of SnapInsta.to_AQPaLPMhHCzlL9Y8E9jIsjt7C4lB0YNHaUpe_m1nVGXgHahFPfpVA_vUpD3RU6F-z2_TvwSj3b30SayD2-HhPhI1denRaMSe8GBz69E.mp4
482
- Copy of SnapInsta.to_AQOuyKoHL6ileKQvw24S_-gMVQa1lRZxogMFHTZjSm6vDRRDk5XKdYyW6139o3IMDXIiyNaJyDR1k_VyBa7KGqw3bcq9_7GVGQHFZZ4.mp4
483
- Copy of SnapInsta.to_AQOYh3ClXmKci--LzYCAE1g5iNaCLJaHfNKxds4IwPAVo-X11i_r9CcFk723CeAQ2yuz0Z7PdSdG5333zhhXKainaqaGW1u9gTiIX8Y.mp4
484
- Copy of SnapInsta.to_AQPCP8-5E-0NrkauJafpI9hmYPyqZigla6I40_Pp9pYl37I-8RkbSisvCw63EwOze0qTw8vKGlF7bmbY2_rBIXQMEEM2q4NJnpoL1Uw.mp4
485
- Copy of SnapInsta.to_AQP3vtFPE4KDyZAI5aZx7Sa72zdG3-26IU2XU7pSLYwvtWJTYGm5ZgtvRDKInfk9LzI2qpOLc-uoKbxyIDao5gXxv5qGOAvWGkjHTIM.mp4
486
- Copy of SnapInsta.to_AQP0dc2gM7f4-3LD6t8iH_j82HKYBfK8OoTD8Z0ToGPG31u4J8mYj2HgFtJbPXHe4tagnT3AynzXWdTxEsaP3LAJMJvn10Mmsm_b7aI.mp4
487
- Copy of SnapInsta.to_AQP-8dmhdDh2EO45RT66-UwOav42m0DzTMyk-cDMUpuvF5AwjKDJM1P2MP7NSIkhWsg5KKf3Iv_-9tr8s3VSYNrE4WywuBudnbPDmFE.mp4
488
- Copy of SnapInsta.to_AQOVcPUuMZb0KohpbbOZDweMADzsVpIsqqN59REGQXsJ0nHXHxxfS8Cfqb8wXM8i8cR4z8h2QksbCjoJ4x3mnsha00Kb_pKE8Wkb6t0.mp4
489
- Copy of SnapInsta.to_AQPcUf3rzkjVNkmR4eICnRJdkZQyEBmAZpmv2itAqcvo0mYGkaiWd1E0lOUwyq6JOKfafj3P0yWIzN1lKRChRB6L9-nXBuBJQbdzNL0.mp4
490
- Copy of SnapInsta.to_AQPKUXFZHr4GjHjmYIVdRrPn43mEv4FuCScCXo4ZZtRyDMxtpDnCe5y3K1XzUKB4Resqc-4Xqd5ZoNORgcFqHTk-O6D0HCWIA2p4PoA.mp4
491
- Copy of SnapInsta.to_AQPe5LWF3BxysTPxiPt4G7xxzJ0O_6BTxDLBvgRBXs3WcxoUIQIDtZB4D1PK5k8oTy5-QV3mAmna37rSkIIx313742PQs5F3AOGJFEw.mp4
492
- Copy of SnapInsta.to_AQPdT9Fy4720t-MRnJN63HXCGZclC3aQEiAXj1rUOYYl8hEKmAOwhX6QwzgQjgfbxp7U3yBl0vJYe9Hs1GPMI-lZ.mp4
493
- working.mov
494
- Copy of Sunset View Balcony.MOV
495
- Rooftop View.MOV
496
- Copy of SnapInsta.to_AQMt6OIXFM3AepSklXmlydAKfY67KB3n25bCblD7hnaamECoYmXq-kCrhSy2G-LaPeJyxo7TZFtf7liF_2nsN2kS4qlnT7265QVa6xg.mp4
497
- Copy of SnapInsta.to_AQO1ePTrmLQkqeJHeM5ZXPkLbCKEuX1JwduJmUR_ZFIT8m5TIIolHTm1S5S4l3dPlNMNhT5zpBk-t1Fcl5KhGewmyhM5q2CyjA1zj9o.mp4
498
- Copy of SnapInsta.to_AQPl6nGIbYJLtoXtULbX1d_lIYM1Ja8R03wUEUVnHQdhJYgMb55E3lb-T7lZMUBuO6xIcIdHf0Odg2CWrLT8X1_u6k5dQfuNq2Fe--s.mp4
499
- Copy of SnapInsta.to_AQPd5lv2ryk5zwzwvCPfJ_jgoFUep3u5y0M4KYgc0uSiWdNl2HmyA-6pHu-AvhRPHiqoVSUucIWEbeidwtERgz1Bb1-HCMAlWm6ccb0.mp4
500
- Copy of SnapInsta.to_AQPhhon3KqxORKlyNsNC5cxnmaqmi5EeScdRfDkQnjxzyR_86ubWQkYw9Vph-9qeCbdjX09ChXCOrMGgwsBWs_mlgyj9AJA1sLoqGdo.mp4
501
- Copy of SnapInsta.to_AQPjEvrnqY29gL6RSh-wCeL3Wdl9O-ah3tebbozcRYXTLbjsvhdjLzyLe4l-4qZLVISE_x5erjwDrS5xdZXgAJdRtKfbcfQd_HAtM-o.mp4
502
- Copy of SnapInsta.to_AQPIP4uJRsS2x0XS0gX8Ix48hMkQv-ve0o1ZJZmeFFfPGQlGQ4rZzvht43yE_1g1aLYcobjXE4Z_xLRMAFtuAgUrzxQYopY5ui4eQT8.mp4
503
- Copy of SnapInsta.to_AQPdefY3LXX28f-AZnDi5v8LaFIDe7i4O3q-usglx4Z6puv7qW3xkc7YKVX8FJfK9SWEZ2yA3p951jCMEHK3mRzFJVDwrbN-u5DFffA.mp4
504
- Copy of SnapInsta.to_AQPKHZdQy0QDbEKJMvMK7Rrll2Lr8vw6uFZPbNGkKbYaFBkTEQLG-CIGE_qnpKOFCMFqDhG1_BMKOyVmU5hYwxhKBOqviLQzXn94PFM.mp4
505
- Copy of SnapInsta.to_AQPmpqhpY3pfvPbFrfuEIh6KXny60lRPgXan61CIChQp4wCAzlmuUkiQYImHoVumLNdavy0ZJ5brbv1AI0Mw1_70Bqu3hX-Ad65-fhc.mp4
506
- Copy of SnapInsta.to_AQPOVsfm2nARUwP4UPbhFK6oy1SwqNC3DB5-V2pkJ2VC8too83aJioMNlzpUsGPjI8Eax_moNTf42ZmG6J-MKaTMZssvn_J-lqCOi2o.mp4
507
- Copy of SnapInsta.to_AQPPK-K6P28rP6Oi7XpsJRwQh5ORmm5KB5V3DYZispKHBNuUCidrAPbqRMI7VzLXATfScpcNF3qbu-9PiCo4hN_obMVMIB2WtYokY7Y.mp4
508
- Copy of cars.mp4
509
- Copy of SnapInsta.to_AQPqdMmXNxyr_MoE6ViDpYXJPt1slINEwXQs1lwK68pUzj5zzPihOl6dNTfvWxPZbIe0AI03gMz0MqR4NPSMO0qivdxCziDUPpS1uqA.mp4
510
- Copy of SnapInsta.to_AQPo7Wy_j7NEEjW1HToI7w8A76LDcB8nlY_JpSOvyf3HT5q0jfQnXuQNYxtHGuHVDJX48D4gJxY0lmt91gPlDIIXNJgsz2eFRzfdzWk.mp4
511
- Copy of SnapInsta.to_AQPPl9rtJLviB0jr-raGJJ3x3tbkQyDPnRWv71P0T9z4zyfEV6zp67jFpAE2Qt9B0Bpk_s352tKhtIEqZZpRyGKfQWb8ei8aZabjRzw.mp4
512
- Copy of SnapInsta.to_AQPNjRl907AIGr7mckkIiB-8EKMjnxM-M3pkGt8McSnhcKmJuQDHvLbwQQGScYWi5aIsGfbHv2SpgXZwFeV9mREF0VNdAGOHalqR_ik.mp4
513
- Copy of SnapInsta.to_AQPnwbFnBhAUQdg9ANIFPb9EbGvEy_HoSgM6iLrqur64Wov4UYnqSbePt8oOhrCEzna7KJsjKqEKeiJ4XzxE_NAbOw1UrUXWz8qjde0.mp4
514
- Copy of SnapInsta.to_AQPpavBgWBPWhwUQvi4281NToZZekb38IVkTCQbjbqxHpba3lrSzB_Rc77TjoFQyEDNk6kc1hLE_b2fHaMTGzYOCV5b8IORTkYgCS3Y.mp4
515
- cars.mp4
516
- Copy of Big Yacht 2.0.mp4
517
- Copy of Big black yachgt 02.mp4
518
- Copy of Big yacht white.mp4
519
- Copy of Big white yacht 09.mp4
520
- Copy of Big luxury yacht.mp4
521
- Copy of Big White Yacht 10.mp4
522
- Copy of Big black Jachts 08.mp4
523
- Copy of Big yacht 07.mp4
524
- Copy of Best view from Yacht.mp4
525
- Copy of Big white Yacht view 03.mp4
526
- Copy of Blue Yacht.mp4
527
- Copy of Blu & yellow yacht.mp4
528
- Copy of Copuple on yacht 02.mp4
529
- Copy of Black Luxury yacht.mp4
530
- Copy of cooking on yacht.mp4
531
- Copy of City view from yacht.mp4
532
- Copy of chilling Yacht.mp4
533
- Copy of Blu lights Yacht.mp4
534
- Copy of Chilling at Yacht movie time.mp4
535
- Copy of Chilling at Yacht.mp4
536
- Copy of Family on Yacht.mp4
537
- Copy of Gold yacht.mp4
538
- Copy of Green Yacht.mp4
539
- Copy of Copy of Yacht3.0.mp4
540
- Copy of Driving yacht opn ocean .mp4
541
- Copy of Ferrari yacht.mp4
542
- Copy of Driving yacht.mp4
543
- Copy of Copy of Yachts 2.0.mp4
544
- Copy of Copy of Yachts under the bridge.mp4
545
- Copy of Copy of Yachts at night.mp4
546
- Copy of Interior Ycht 3.0.mp4
547
- Copy of Greey yacht.mp4
548
- Copy of inside white yacht.mp4
549
- Copy of Interior on Yacht.mp4
550
- Copy of INterior yacht.mp4
551
- Copy of Interior Yacht luxury 2.0.mp4
552
- Copy of Jetsky on driving on yacht.mp4
553
- Copy of Interior of Big white Yacht 2.0.mp4
554
- Copy of inside yacht.mp4
555
- Copy of Lamborghini yacht.mp4
556
- Copy of Luxury white yacht and view.mp4
557
- Copy of Luxury Yacht 09.mp4
558
- Copy of luxury yacht 02.mp4
559
- Copy of Luxury yacht 03.mp4
560
- Copy of Luxury woody Yacht.mp4
561
- Copy of Luxury white yacht night.mp4
562
- Copy of luxury place Yacht.mp4
563
- Copy of Luxury yacht 07.mp4
564
- Copy of luxury black yacht.mp4
565
- Copy of luxury yacht 05.mp4
566
- Copy of Party at yacht.mp4
567
- Copy of Pardo Yacht.mp4
568
- Copy of Ocean view.mp4
569
- Copy of Purple Yacht 02.mp4
570
- Copy of Party at yacht 02.mp4
571
- Copy of Mountain view from yacht.mp4
572
- Copy of Purple Yacht.mp4
573
- Copy of POV yacht.mp4
574
- Copy of Luxury yacht.mp4
575
- Copy of Luxury yacht at night stading.mp4
576
- Copy of Sirena Yacht 88.mp4
577
- Copy of Sun set Big yacht 02.mp4
578
- Copy of Sunset yacht 04.mp4
579
- Copy of Sunset Yacht 07.mp4
580
- Copy of Sunset view from yacht white.mp4
581
- Copy of Sunset On Yacht 07.mp4
582
- Copy of Sunset yacht.mp4
583
- Copy of Rrolls & white yach.mp4
584
- Copy of sunset view from white yacht.mp4
585
- Copy of Sea Yacht.mp4
586
- Copy of The view from big yacht.mp4
587
- Copy of Three Yachts.mp4
588
- Copy of The view from yacht 3.0.mp4
589
- Copy of The view from yacht 04.mp4
590
- Copy of The view from yacht 08.mp4
591
- Copy of The view from yacht.mp4
592
- Copy of The view from yacht 03.mp4
593
- Copy of The view form big yacht.mp4
594
- Copy of Throught Yachts.mp4
595
- Copy of The view from yacht 2.0.mp4
596
- Copy of View of Party Yacht 2.0.mp4
597
- Copy of Two Yachts 2.0.mp4
598
- Copy of White Big Yacht 03.mp4
599
- Copy of View from Yacht 2.0.mp4
600
- Copy of View from yacht.mp4
601
- Copy of Walking throgh yachts.mp4
602
- Copy of White luxuryu yacht.mp4
603
- Copy of Up on yacht ocean view.mp4
604
- Copy of White luxury yacht 04.mp4
605
- Copy of White and blu Yacht.mp4
606
- Copy of Yacht & helicopter.mp4
607
- Copy of Yacht 4.0.mp4
608
- Copy of White yacht on oncean driving 07.mp4
609
- Copy of Yacht & helicopter 02.mp4
610
- Copy of white Yacht night .mp4
611
- Copy of White yacht luxury night.mp4
612
- Copy of Yacht 02.mp4
613
- Copy of Yacht & helicopter 3.0.mp4
614
- Copy of White yacht on ocean 09.mp4
615
- Copy of White yacht at ocean.mp4
616
- Copy of Yacht luxury night 04.mp4
617
- Copy of Yacht at sea.mp4
618
- Copy of Yacht 07.mp4
619
- Copy of Yacht night 2.0.mp4
620
- Copy of Yacht date.mp4
621
- Copy of Yacht 08.mp4
622
- Copy of yacht luxury sea.mp4
623
- Copy of Yacht at ocean 03.mp4
624
- Copy of Yacht at night.mp4
625
- Copy of Yacht birthday.mp4
626
- Copy of yacht stairs.mp4
627
- Copy of Yacht on ocean 04.mp4
628
- Copy of Yacht opn board 04.mp4
629
- Copy of Yacht night red Led light.mp4
630
- Copy of Yacht tools.mp4
631
- Copy of Yacht show.mp4
632
- Copy of yacht on board.mp4
633
- Copy of Yacht standing.mp4
634
- Copy of Yacht standing night.mp4
635
- Copy of yacht on bard 04.mp4
636
- Copy of Yacht.mp4
637
- Copy of Yacht view 03.mp4
638
- Copy of Yacht view night.mp4
639
- Copy of 1.mp4
640
- Copy of Yachts under the bridge.mp4
641
- Copy of Yacht walkingmp4.mp4
642
- Copy of Yacht white on board.mp4
643
- Copy of Yacht3.0.mp4
644
- Copy of Yachts 2.0.mp4
645
- Copy of Yachts at night.mp4
646
- Copy of black jet 02.mp4
647
- Copy of Birthay gift on jet.mp4
648
- Copy of black and yellow jet.mp4
649
- Copy of Black and gray Jet.mp4
650
- Copy of Black G class and Jet.mp4
651
- Copy of Black and white Jet interior.mp4
652
- Copy of breakfast jet and view.mp4
653
- Copy of Cars & Jet.mp4
654
- Copy of Cars & Private Jet.mp4
655
- Copy of Black jet view.mp4
656
- Copy of Booking Privat Jet.mp4
657
- Copy of Black Jet.mp4
658
- Copy of car and jet waiting .mp4
659
- Copy of Black Jet landing.mp4
660
- Copy of blu jet.mp4
661
- Copy of blue jet.mp4
662
- Copy of chilling jet.mp4
663
- Copy of City Lights and Clouds at Night.mp4
664
- Copy of cars and jet 02.mp4
665
- Copy of chilling on jet 02.mp4
666
- Copy of Cars and jet 04.mp4
667
- Copy of Cloads View.mp4
668
- Copy of Celebration on jet.mp4
669
- Copy of City Night view 02.mp4
670
- Copy of champagne on jet.mp4
671
- Copy of Chilling on Jet.MOV
672
- Copy of Enter on Black and yellow Jet.mp4
673
- Copy of entering on jet 02.mp4
674
- Copy of flex jet.mp4
675
- Copy of Closing door of jet.mp4
676
- Copy of Dinner on Jet.MOV
677
- Copy of clouds from jet.mp4
678
- Copy of enter on jet 03.mp4
679
- Copy of Cloud view froim jet.mp4
680
- Copy of female on board.mp4
681
- Copy of Gray and Black jet opening door.mp4
682
- Copy of Interior + View Jet.mp4
683
- Copy of Gril opening door of jet.mp4
684
- Copy of gray jet.mp4
685
- Copy of Interior Jet 04.mp4
686
- Copy of Getting on a private jet.mp4
687
- Copy of Green Jet.mp4
688
- Copy of Interior Jet 03.mp4
689
- Copy of gray & red Jet.mp4
690
- Copy of Interior jet 05.mp4
691
- Copy of Interior Jet.mp4
692
- Copy of Interior of Jet 03.mp4
693
- Copy of Interior of Jet 04.mp4
694
- Copy of interior of jet.mp4
695
- Copy of jet at night.mp4
696
- Copy of Jet at night 03.mp4
697
- Copy of Interior jet.mp4
698
- Copy of jet closing windows.mp4
699
- Copy of Interior jet white.mp4
700
- Copy of Jet 360.mp4
701
- Copy of jet night.mp4
702
- Copy of jet interior white.mp4
703
- Copy of Jet interior.mp4
704
- Copy of jet interior 05..mp4
705
- Copy of Jet on Airport.mp4
706
- Copy of jet interior 04.mp4
707
- Copy of jet interior 05.mp4
708
- Copy of Jet landing.mp4
709
- Copy of Jet landing 02.mp4
710
- Copy of Jet interior 02.mp4
711
- Copy of Jet rotationg 02.mp4
712
- Copy of Jet start from eath.mp4
713
- Copy of Jet out night.mp4
714
- Copy of Jet starting 02.mp4
715
- Copy of Jet Return.mp4
716
- Copy of Jet starting engine.mp4
717
- Copy of Jet pilots.mp4
718
- Copy of Jet services.mp4
719
- Copy of Jet starting.mp4
720
- Copy of Night view from Jet.mp4
721
- Copy of Jet View.MOV
722
- Copy of jet view 02.mp4
723
- Copy of Lamborghini and Private Jet.mp4
724
- Copy of Military Texture Jet.mp4
725
- Copy of jet view 03.mp4
726
- Copy of Morning view from jet.mp4
727
- Copy of jet view morning.mp4
728
- Copy of Jet view night.mp4
729
- Copy of Porche & Jet's.mp4
730
- Copy of Rainy night .mp4
731
- Copy of open door of jet.mp4
732
- Copy of Rain View.mp4
733
- Copy of Opening door jet.mp4
734
- Copy of Ready for Jet.mp4
735
- Copy of Spray parfume on Jet.mp4
736
- Copy of services on jet.mp4
737
- Copy of Pilot thumbs up from jet.mp4
738
- Copy of rain night jet.mp4
739
- Copy of sun set 02.mp4
740
- Copy of the view from jet.mp4
741
- Copy of The jet View.mp4
742
- Copy of sunset jet 02.mp4
743
- working.mov
744
- working.mov
745
- Copy of jet on board.mp4
746
- working.mov
747
- working.mov
748
- Rooftop View.MOV
749
- Copy of enter on jet.mp4
750
- Copy of Jet starting 03.mp4
751
- Copy of the view 03.mp4
752
- Copy of sunset jet.mp4
753
- Copy of The view from window of Jet .mp4
754
- Copy of Sunset view from Jet.mp4
755
- Copy of Starting the fly Jet.mp4
756
- Copy of Sunset Jet 03.mp4
757
- Copy of walking through jet 02.mp4
758
- Copy of The view inside jet.mp4
759
- Copy of walking out of jet.mp4
760
- Copy of Views from pilots Private jets.mp4
761
- Copy of The view of Jet champagne.mp4
762
- Copy of walking on jet.mp4
763
- Copy of view from jet.mp4
764
- Copy of The view of Ocean from jet.mp4
765
- Copy of view on night.mp4
766
- Copy of view of the jet black.mp4
767
- Copy of white jet.mp4
768
- Copy of Welcome to dubai Night.mp4
769
- Copy of walking to the jet 02.mp4
770
- Copy of white jet 02.mp4
771
- Copy of white Jet landing.mp4
772
- Copy of white and blu jet.mp4
773
- Copy of white and red jet.mp4
774
- Copy of walking to the jet.mp4
775
- Copy of Walking through jet.mp4
776
- Copy of Black Lamborghini.MOV
777
- Copy of Balcony View day.MOV
778
- Copy of Backstage Club.MOV
779
- Copy of Beach view 01.MOV
780
- Copy of Beachparty night.MOV
781
- Copy of Black Lamborghini 02 View.MOV
782
- Copy of Balcony view 02.mov
783
- Copy of Calisthenics at Home.mov
784
- Friends.mov
785
- Copy of Cars 05.mov
786
- Copy of Cars race.mov
787
- Copy of Cars 04.mov
788
- Copy of cars 06.mov
789
- Copy of Brabus.MOV
790
- Copy of Cars 03.mov
791
- Copy of Cars.mov
792
- Copy of Cars 02.mov
793
- Copy of Clubbing 02.mov
794
- Copy of Clubbing 09.mov
795
- Copy of Clubbing 05.mov
796
- Copy of Clubbing 07.mov
797
- Copy of Clubbing & Wins.MOV
798
- Copy of Clubbing 03.mov
799
- Copy of clubbing 10.mov
800
- Copy of Clubbing 06.mov
801
- Copy of Cliff jumping.mov
802
- Copy of Clubbing 14.mov
803
- Copy of Clubbing champagne.mov
804
- Copy of Clubbing 11.MOV
805
- Copy of Clubbing 16.MOV
806
- Copy of Clubbing Birthday.MOV
807
- Copy of Clubbing 13.mov
808
- Copy of clubbing coctails.MOV
809
- Copy of Clubbing 19.MOV
810
- Copy of Clubbing 12.mov
811
- Copy of Clubbing 18.MOV
812
- Copy of Desert.mp4
813
- Copy of Clubbing Strobe Lights.MOV
814
- Copy of clubbing.mov
815
- Copy of Clubbing Friends.mov
816
- Copy of Clubbing Red Bull.MOV
817
- Copy of Clubbing night view.MOV
818
- Copy of Clubbing04.mp4
819
- Copy of Clubbing night lights.MOV
820
- Copy of Clubbing Girls.MOV
821
- Copy of Clubbing Night.mov
822
- Copy of Dinner.MOV
823
- Copy of Drinking Red Bull.MOV
824
- Copy of Driving Cars.mov
825
- Copy of Driving Porche.mp4
826
- Copy of drinking.mov
827
- Copy of Experiences.mov
828
- Copy of Driving Night.MOV
829
- Copy of Driving Cars 02.mov
830
- Copy of Eiffel Tower.MOV
831
- Copy of Helicopter view 01.mp4
832
- Copy of Ferrari 01.mp4
833
- Copy of Ferrari at Night.MOV
834
- Copy of Helicopter View.mp4
835
- Copy of Jumpin pool at Night.mov
836
- Copy of Jumping on beach.MOV
837
- Copy of Ferrari Night City.MOV
838
- Copy of Jet ski.MOV
839
- Copy of Jumping from rocks.MOV
840
- Copy of Gym workout.MOV
841
- Copy of Lambo shooting fire.mp4
842
- Copy of Morning Chilling.mp4
843
- Copy of Laptop working.MOV
844
- Copy of Motorcycle Sunset.MOV
845
- Copy of Lamborgini.mp4
846
- Girl in bed.MOV
847
- Copy of Night Chilling.mov
848
- Copy of Morning view.mp4
849
- Copy of Luxury Food.MOV
850
- Copy of Jumping pool night.MOV
851
- Copy of Night swimming pool.MOV
852
- Copy of Outside.MOV
853
- Copy of Party & Money.MOV
854
- Copy of Night Rooftop View.MOV
855
- Copy of Night City.MOV
856
- Smoking.MOV
857
- Copy of Paris at Night.MOV
858
- Copy of Nights cars.MOV
859
- Copy of Night outside.MOV
860
- Copy of Party house opening champagne.MOV
861
- Copy of Pool friends.MOV
862
- Copy of Pool indoor.MOV
863
- Copy of Porche Night Paris 02.MOV
864
- Copy of Porche Keys Night.MOV
865
- Copy of Partying outside.mov
866
- Copy of Porche 01.mp4
867
- Copy of Pool indor02.MOV
868
- Copy of Porche.mp4
869
- friends.MOV
870
- Copy of Porche Night Paris.MOV
871
- Copy of Rooftop View.MOV
872
- Copy of Sea View.mov
873
- Copy of skyscrapers view.mp4
874
- Copy of Recording Gym.mov
875
- Copy of Sunset Walking.MOV
876
- Copy of Sea View.mov
877
- Copy of Rolls Royce.MOV
878
- Copy of Rolex.MP4
879
- Copy of Training View.mov
880
- Copy of Walking through cars.MOV
881
- Copy of Walking corvette.MOV
882
- Copy of Walking Paris at Night.MOV
883
- Copy of Walking Porche.MOV
884
- Copy of Surfing02.MOV
885
- Copy of Waiting Dinner.MOV
886
- Copy of Taking Photos.MOV
887
- Copy of Surfing.MOV
888
- Copy of Workout Gym.MOV
889
- Copy of workout.mov
890
- laptop.mov
891
- Copy of Watches and DJ.MOV
892
- Copy of Working at Night.MOV
893
- Copy of Working pc 02.MOV
894
- Copy of Working Pc.MOV
895
- Copy of 1.mov
896
- Copy of Walking.mov
897
- Copy of SnapInsta.to_AQM9gKyB9RffZwHGX4cJsWobiFBN8HmKK5wUkokHrxabe-bfwYGlE5YzEgiLo9I-Kvqqu-4fcGYUz8HRFPQwxEIO0cHJV-YXxp9WsQE.mp4
898
- Copy of SnapInsta.to_AQM2wItVQc4lNxX9WePv5vACM94oPFbIkzg2eMVL5jvbmmbBY7X3qaEusb4VHH5owdcEgMloVAMQiKTq2QaqN75QcjWfFI0FXNA60KQ.mp4
899
- Copy of SnapInsta.to_AQM3IL1m_StcI1k0lDtIpVprKv7VIa1ojdT7QdCoDIJRA63w-WMHQperAscWiBjXBkFrMmzGwzSAguu3hndUk3j__JHUZOPQg7geIEI.mp4
900
- Copy of SnapInsta.to_AQM3vJOteJzhLH4N1xByWgccA3LQtv0HZyQUyO9H5Ka0CE9hBGE8ahMH-l2Oy1XYeHL7OIHa0kChBgXFqmSc7qxzhc5cr10o1kfTzFc.mp4
901
- Copy of SnapInsta.to_AQM-rSVIU3fi3sUrTMpvxFf7xIoQS-00_VYvw5OrswDJr0bffbbLAeoAfDz25508aMlIXgqjzV3okhvyvVHQYr06GwnK_tAGS2p6T7Q.mp4
902
- Copy of SnapInsta.to_AQMfly9VLE05_3Q5o44y4zv_6pgxTgejSntret4ZIE8L_WK0Ca-g_i1XXgljFYyCIwXQ8s5uk0NezylImRntnudmjs021jdPIO-YuwA.mp4
903
- Copy of SnapInsta.to_AQMIRqwzhw2L0wXkom8hG-XZYMk3bjDTubeqNa1n6EHSVQ8y3YQKAbdvyLdRzr-UFqU9srR76Iu-oneM3l1s4NSKx71uKyfO2HqfX4c.mp4
904
- Copy of SnapInsta.to_AQMady5bZSGaaA9STOXJdLfzB-DaP4JJw3_372B2bMaKdJ5I9mWw_192BleK9FtJvj4sVwjnOXLanKaPFk6kHua_5MKMXuiOaptR9Tk.mp4
905
- Copy of SnapInsta.to_AQMaUMWqFVjl2s0qA-FlweXqIoEtfY6dM5vkR727pEbTSawLx8nwaJq12lu9oFaRBNrXng1oqeKDLrtjKcUJNbzoZ9tLlVqf_xUPtFU.mp4
906
- Copy of SnapInsta.to_AQMe6L1GWM4K0ruc00wmQGlyO8GC-bEkb-ae5rner_dGPZxzBuXsqUZzgfjtVPlpZPFHMvsmiTYx26g6ogoWObkieQmWaWWQOvAmdb0.mp4
907
- Copy of SnapInsta.to_AQMD9W0V3kKUV7k6LZfsHNRYjbBNlwVjUU0sZ3hWkxiXVM19THGT-hcpOKl3zZ1K9yARswigoXEXYvMqdYxxQ3UND7JmE37avLsS9tY.mp4
908
- Copy of SnapInsta.to_AQMhz0IXIwcaA_rN0ap1agGBwUHSEkl7WLdvKdPR4hRfZLTSwwg6Mc48OsEAB-lShLvbocXW3AV4S3E6SLOnsy3-3a6g_aaYnC6_er4.mp4
909
- Copy of SnapInsta.to_AQMBAGy14oXJZhnhovfilQh5mmDhQigLvHRuAMyHxepkZRP_tB7pkHpP5asYpL60xDtgUUtTliDarT63nRUDrpqyO9K2bKzfcB7F77M.mp4
910
- Copy of SnapInsta.to_AQMixWX3ILZinwMBMOu7Tj0iyGJC81rAUkVX0h2AhNjVC2KAR9HfzHo6TlOYUxZcXkyO0xeGf1mSyXwvAO58SiNvh5_xnR6oExodxSE.mp4
911
- Copy of SnapInsta.to_AQMevy3Zp-4ENKLpyULNrQqh6dLL_-ndWwqvlA2W44ZQgiZ06ZVIPjDlwCdFxG2F6vDElWJGsqaBhbtgqOf1wDRBbegKv62qmLTYq_A.mp4
912
- Copy of SnapInsta.to_AQMYsU02r0iTPflS06eq5HbJyjchTXtH8j3EsfMBmwDIaHAef5CQ444aY0ScxGLlEy1N-W61Wm2Y3ByvRKCNt5ix.mp4
913
- Copy of SnapInsta.to_AQMRka9XWTHTGVr6roin7gUr2sosmrpZAL1jlt8akIeAqt4O_Qyjeb8lnc39MtgDJrdgNRDK1Laz-PZfsbRppzL6zbuNKNZJ_6RYTL4.mp4
914
- Copy of SnapInsta.to_AQMphtTMkqPNrkQf7JdUiQxNI60DrXE3xKbGPuHmPPq0x5iXs1z5ngC2mb_beQPh0rcvYiI6BSI8wqJFRYOGNZCicBEauJ5Xo116G40.mp4
915
- Copy of SnapInsta.to_AQMZdPtx1kz-9m4jECgOMePiL8_wgyFhaKKa161eVfuLRTZjVEeu7yjGLtQPA9ir7Y3wnKeLTiPwUenOyMHpPnJS.mp4
916
- Copy of SnapInsta.to_AQMJtG23t59ISVylNXx735eG1qHfTnf4evCT7seeO-6yBOReFGBfXch3ZVFJhIxIf9F6YNf7IXzeOO9jd5e4M-GcQev-qDOd3xWfu68.mp4
917
- Copy of SnapInsta.to_AQMRXtqrUrUbGcK9YbIKs_8M1JmxKmDDZQmjEibL264XTuUA3FeUSsIFPQloL4Wq23FEhQ2Qs1IqlgiNgyAvEtvYDespbDSXLeZMo_k.mp4
918
- Copy of SnapInsta.to_AQMscdyskpPKrunOvjZvp2PBjyQTDMd36SPyb-Nq4lR4qSauFTFYf1RSOKYhYoxOxHwbfRx9nNKsttZojR_QPTqAeQ1ZuQy-LfkR5qc.mp4
919
- Copy of SnapInsta.to_AQMygLLqZR2pcgUR1xIItBafbreOQFeUwyqZOv5QrgiejradJJSo8Ds5uKE4DYjWNs9FVLClig0iRTcXqiGtTZOr6HCMYgm4fcSAbPM.mp4
920
- Copy of SnapInsta.to_AQMyio5XAeOKjF9n_ilZbjsXt0QDTYgXpn89h-JEAxyhi43f5EwtkaTx1RMOwWMYMIyCzx_J0nLnzUkcPttggBGifYmPxoWkGVXq0v0.mp4
921
- Copy of SnapInsta.to_AQMUP3mJarc7nRDIiyTx71Xqc9KwmN8vfrSh0B4Zuj_jTo5iescLCdRaz9phBMFG_U9ErjVObCkBx3z8-e9shmMaKvbfyhiVw2KcnEc.mp4
922
- Copy of SnapInsta.to_AQN9UpdiT-bMDd11_GqgciB-5aXCdpr0o95KW-_xTsXgr0YYl-PtAnYglRD9mxBCCh1gGbl9ohd9CbhoZz_lCPmKCKG-rGdF6120_iE.mp4
923
- Copy of SnapInsta.to_AQN0UpZc2KMOfHHIRo8s2p1zBcLbLOQSuL5iCp3p4-mT9PFDA8O6aiipRrxSRDQEEWP-oDiGnFKgbJtkfXwmIcWgCPzd7RYyySysxDk.mp4
924
- Copy of SnapInsta.to_AQN_vvfxr0FH3B3EDAUlsMlMYv8pfY2CboeHNAyjHMM1Gp0c_MzvK6xPMd_BaI705qR5VsBL2xvuWATleNc9ApXvfi-vVLU_a66TRN0.mp4
925
- Copy of SnapInsta.to_AQN9QoPayirxh8JuAbSWaPXLOk4CzA7iROKfyMOiaa0Lhk_5QCFXrXx7e8ZML_6HwJg0_2MnusD_imCmEq6RzV6j1oc4M1qCUhMxumY.mp4
926
- Copy of SnapInsta.to_AQN1Q_lCMz8w2rSR38JC2-lGxIb824FydZnZO2DocveqNreyIzqcDK0B8n9C4lxDCJd3Bd4tGQWVVDthV7XNMVzFRfpIe9LSg0fEmgA.mp4
927
- Copy of SnapInsta.to_AQN-DnEWgi9NonpZ3tk7h9T1iWc-ERctOAnZcrx_2kITTmadZqIRQfJRCkLK2zzBBawqVedhMTYXfP12p3R59ePbRV-13Ifh7F16tvA.mp4
928
- Copy of SnapInsta.to_AQMzh1eWMKv6jspdRMupCEj2vl2Isf5wQoZnJ6b4rlZpKHgtVxef0PIzwaDmdOSP9DkF4H4W1WQbm065RrAb-m--0__1VKlS4caSY7I.mp4
929
- Copy of SnapInsta.to_AQN341ybRObDcV0YIAKLywgqCkH08UU5Ejwmyj1Fsyq1RsbweNUa-CFe7vXf8p4nKYjmA64grom4iSlAGZygXkjiumT33793VP9ZKg4.mp4
930
- Copy of SnapInsta.to_AQN0QwGmCrOEulODiWRp0McNgdHMwaphmpN34tiLtuo9FalK_7LG2O7Jip2ueDfsAtF_QJQFRdTeXGZFxIV7OfvjwTPX37eupmlY32k.mp4
931
- Copy of SnapInsta.to_AQNgW7_1euWAdhEZD9SGX2EhhT2MK9hHMG7E814UWVAubGeo_zO1vvdrqaCeX1O4PdLVdVmqsTIdZj_QjNyDdWs_cB9Wrjin4ftodIk.mp4
932
- Copy of SnapInsta.to_AQNFtY68SoqhdIheHHvszbK9YEXobdXg2gG9dyUE3UKPSpqVoLtateYUUNcT_KQBbW72X09SZ3A01E34BhkD-rlRUqw4X82Bqf3fzSc.mp4
933
- Copy of SnapInsta.to_AQNE6tPyHQY6lk7oTAbviV3-kJgNw8QKs8xkj1PNo4y3lhJwsR0pUdZoyPfZ2CLka2EPPWjX3ZhWeJ6hhfKVxT3wvZsNVCUxS2ZJ-0M.mp4
934
- Copy of SnapInsta.to_AQNbEri2vkwuQN_npC-GH5zPlEVnst7i70xQPDVQz2S7u0SwpVhefjcHbUC9OhsZ1lt4cqm4xjHlhziUOtzNyUGTslu4dO0mX1I5NVo.mp4
935
- Copy of SnapInsta.to_AQNIiy_P-jZMkrOWfDSH3h9WViFdiGEMaIRvywRZweTWaUWSZhjSibBiRxBkOSIHcj5zUUkYgAVOb2V-2EpcQ_b11yuFN2bRQcs6Lws.mp4
936
- Copy of SnapInsta.to_AQND0mR2ow6vTuVnNwcwrutmYgsFhnSMAGexig_PLKeM199pRWNdMsLDY6FaH0PlGlvre2yMm40EtsqVTjNM5M80q1BlkmIxktM3g-Q.mp4
937
- Copy of SnapInsta.to_AQNEwP2t9F925rOYEU51Jq0LlVPIRBa9nCbCD_G-vXWKMToxJTjFXzn6Vo2PBKbADvKjFAd8biFRnOSz496WgNpu_AqYyg4qwLE_k5o.mp4
938
- Copy of SnapInsta.to_AQNbG4186TN2EY484_GsMwM3q_Ri0DBbGV7uS8fi2aCdfIvikR1NP1vxyMK6QIjhX90Sdvg0209SJ654wIeBxJA1kCa62QwDj5W47r0.mp4
939
- Copy of SnapInsta.to_AQNCVN6-UqXVT91ReGjppfpeCCKWQ8oto_xSRPicC2EuSs9CDRfBzPBj8agrKOmYs45Q8iEjPGrBRs8vilk1f7Lh8x83v9PCvYlmQOA.mp4
940
- Copy of SnapInsta.to_AQNjOIeE5DMFtJN4X2MPhqekhwwFmv3sOF-aMG8kT8bpMvvRchxrs1NAQ1XVz3bUzxRGrjYgLnFQ9ZB_mDwJXSptvtAJRCjnbjrUzmM.mp4
941
- Copy of SnapInsta.to_AQNmsggNL6Yn-t-atp-561xSbGS3FxvjHhGhMP0x46mY0HiPDIEl5CeJ99Ej5KV15o-pIs1Lrs1uMpIkhbNTbTtFOga54wTfhoNjIVo.mp4
942
- Copy of SnapInsta.to_AQNM_rR45m6afGYLwTwwJu5WEYCvQwrHuUqhz0y4CC8lHYdGxWBQkicLvUmJmXufX41pl5c1IBZPg2Kag94ejM7y9DWjF_lx5VbJMfc.mp4
943
- Copy of SnapInsta.to_AQNO8B-n5CjOm1VsxZyv35mPXxzrvEOOl4_4kDkDGNs_fiWjfL9J20swdueppYQSicuaeoYDTS3fJ9MhYLixz1Cdap6Lm0pA4Z2p0Qs (1).mp4
944
- Copy of SnapInsta.to_AQNLLtGp2wew34B6_NV-k3fSXRKlPECoJCbInRFMRcLycpEzAf0_Yva4PD4P8YfnysK_ojHjpuPYDHVnOVTowJ1eR4A9OBXexGdwyng.mp4
945
- Copy of SnapInsta.to_AQNsH7cmpN-yyag_j6vShSrnV1fP8w7lVLNG_O90rl9YnwRmgCGbo3Gi2eEDn2dqNJ1xLubPgV48OMxGYtzqUymbShmP_hxDnRf06dw.mp4
946
- Copy of SnapInsta.to_AQNRr9GhZretTrJHKUK7um6WVi_AtqOyKtUjjO_tz9XtMpTMBiokwN_bc8TC_1-UlhkmxNs2TIugDp0gz2un9AErAma2P8i9WMZ3f58.mp4
947
- Copy of SnapInsta.to_AQNm9topqLNq8f87eVny0iql--ZcmtpRNyfWhOonCcNJ5bqder-1TNedl5lD0bflWEaK0EX0iQC2IuKRscNMcaXRiJbVoQcuEmJjUSc.mp4
948
- Copy of SnapInsta.to_AQNnvvmmaWDo28OXsNB2h_8I3qZdL7a2FLuZfq4WsoOWSNrtL1CF5fC1ybAJDZeVsygDEYzsvOO9d--shsRSeNFBTaqRuw6pzdDiwzU.mp4
949
- Copy of SnapInsta.to_AQNpadk3mZr6Lo66w7inLv2rk98mWubWTZOvs4wsODcZ9Ck_Nuqu4tcA3C0ycO2l1nAE28jo9GGWz8XxlQ0JMTdY0Fehpy8LhTZP8zY.mp4
950
- Copy of SnapInsta.to_AQNK9ZX4i44xF0wV6aS5e1hetjmW_1xtDOtUH1Hnt3Lj5p2vftAXm8tNEccOjhoXwrTJXmSj7OPEWvGCr1GguKg7R6HVYuEnaO8eNbE.mp4
951
- Copy of SnapInsta.to_AQO7ZYIOnI_dqfLq5JdMz-UAp2A0bU037yMYx-2idJb3MBQe-B7D0bvbztnfe_SSc50F6YaZsWHV-XCfUDbFioB9zWA2EU2mizlCRyI.mp4
952
- Copy of SnapInsta.to_AQOAWvgyjvaNR3iY6OXcfS-ig48x72JrkYHZWg-ys0QbyOFqgC9yrrcvJELhdnqAUUXTonBsYDi62mGyBz5gKYiqaNnY2-0_zuHgkks.mp4
953
- Copy of SnapInsta.to_AQO_a7LsTYTpTxO9uVWfJ7M_gsaUBD1Sc5T13uj30TBZjGMMdfcMZRZkbViMTyxT4gsbO349YMAOpoOZOE_RrtA5WEIXOqQmZbyra8k.mp4
954
- Copy of SnapInsta.to_AQO9SLDWuJxHZ79csaRWumqWCkdG2HZqgRqidCvIfzfHbWwKONGL6pgnlxsvlG8K1L8AUrg4TFKglQkY0nBdLQOtWD6nbznizQPgLIA.mp4
955
- Copy of SnapInsta.to_AQNtth1ToGiyOPMlKYe0SBK0WOexby_2Z2ahLta5UaSKUgvuUkH28D0gFfkJE3VLyTIxlZ_KyJI6uvKPw7B9xnwPbYcp1CQCqcE8qSc.mp4
956
- Copy of SnapInsta.to_AQOadKqLUR9JBRbG2Zk_dmd9lMoe2rXha7Apq-aUHJHxlFQ7lUgZcSCRGyGN74CXcQcpgnvBo2nAp3ToPjrxwjXYQHjXypdDkw6bS6U.mp4
957
- Copy of SnapInsta.to_AQO8TSKuH7ow8_GiZ-4BR82mK6JzT37H-wHxSNXvfFNxYM4ASa1Y6QirMFbvskzOaK02pZ1XUQz2PqIn_PYbvXJ3IO0CKI_BuRwcmQg.mp4
958
- Copy of SnapInsta.to_AQNSnOTvPLwxDj_QiOGsjAcgBqmgNyEQpAvWHpyKRJIedBC78EpMM_Xaot3WkV2c9y-LBN2Tf28picdOO8TSD5QU.mp4
959
- Copy of SnapInsta.to_AQO2-e6TiRkzEKV4dM7pNqF97W6VXeRPz09Q2NAcJYlFRV-7UW0MfhMDFJlgr2Ibw5FWOi5NmGaKwt61h38s1YbiuwpN7LUI1Yf3EJw.mp4
960
- Copy of SnapInsta.to_AQO8KTrxuIFslELZs0kFsxceWfvz99S6X4KVfMNYqNgKOInTwnv288yktfaD8YqNs7DvhcLNgVM9s0a75K3SzVueJpeH3iKCnNgMlho.mp4
961
- Copy of SnapInsta.to_AQOPt0j4O-5YX1gv5qENhP__-PsmZYAstd_jzuevnElPoxyIyVgwLhQOXNKjszoQG9X9mL3DpFNFE1GU_iNIlFlAKchFlc7CNrVcRgY.mp4
962
- Copy of SnapInsta.to_AQOFkG140_8Dom5YC6lz0eoNjohs0e_avku_zf-wfm1MlZ-PYgBikxZaYhgHxqteyOAk96Ff5Y4p9h46ybqKmbrt2RJgyuacq8_Xxa4.mp4
963
- Copy of SnapInsta.to_AQOmiorHc8hj3hCfnU3bdZ3fEuPS-tmDVxzLwP5rU7dh4unpCiAaeJMqhBjZ3CRioZxXLBq1haU8fjPYL7h0TwUgos3BW-TpsyUBjf0.mp4
964
- Copy of SnapInsta.to_AQOOJUqh8uPFj_Rh9KQxGdl2Uz-HmYD5PHZRV5jCOsm2QKQo65CPXTshdhe6sGk1qzy-Ff3kjz7doGuyqsHEnbMis_dz7MsexVgbpzU.mp4
965
- Copy of SnapInsta.to_AQOayXIdW15ZLmOiQ2osZ8PI8K5lGqG-3EZzvIMNC8AYHIOc9quCkGq1dR69P4U0pR2GnMjyAWry5hlYfFAYgJ_Y1tMsQOZSfg0NXWo.mp4
966
- Copy of SnapInsta.to_AQOBNKWmK1dqmGYjIIBru-HIVeY4xI0dTPpjuvkLC-2X_6U8-qqvlJNrRA-j5Ghs1yw3yjK14Ijq4C6t12bncs23oB65ETz9wMsKMQY.mp4
967
- Copy of SnapInsta.to_AQOR66XEnFibNdyrRgSrdSkaL0npTqJklaTkZc-T9hOtz1zkKXLiZq_8OMpaQ-RyFnbBsnZye1zcZkeMRQBNo_2WhEYRxyZX0MMv-1o.mp4
968
- Copy of SnapInsta.to_AQORQSyGGwRMdCB12i26XsFVT5zDjlgpBTkUYoB-cs92cTohDCzosOcXIuHUu6veMnHHd4PYXVZuv7XVEuPSiaD5do9UrnHkX9mSQfM.mp4
969
- Copy of SnapInsta.to_AQOsN58Duo-bdep6JkkZrvFTPYAXlejvpqXTz-J_ftO7bYeFrwTb0vb3tLF7mg8B0D-JquZgIO694eXEjHAw0xhFWWItjJf6ycjYkqs.mp4
970
- Copy of SnapInsta.to_AQORyO_rvP1CkzVxZSHqCyIcee3VAo1kAV42zV3XocUu09aJ4H58mDG1iwDSY8YRR9nGnfXKihJ4vOTOxdeWdQ5exMKb3Wq0c73zmFs.mp4
971
- Copy of SnapInsta.to_AQOv_hAltoartNasUQQ4hSiKdCcylPpyXmCbbCbRyHKxjrHCA41awqOMbYnQR-Ti1w0CD1p6w5BwLbXRRgZjJab3NiGolZS3Grltgk8.mp4
972
- Copy of SnapInsta.to_AQOygPFr8Gkln5sb1tV9c8BvTvcadLxgzY9WEUEATwYwI1sd15taCgmmZPegxwW_TkN3_2ThUdKNOPrDdt_MkAA0XEedRyj8tmTHt-M.mp4
973
- Copy of SnapInsta.to_AQOUJAP8mtH4xKT4I39Kq25HUSMVkU2LASaDnh18O47MZ-V3SMH63xD69fQbZFLFXgGhP2Co4zuPuCuWv0_FPi9PPTFJl9KjgGrC0vM.mp4
974
- Copy of SnapInsta.to_AQOy7o8vIxP7Y6xI3NS91ItFhlos5bBZRGxf43jlP0hoXwry4JkNx6bvrzAkwi2o9_5x6JjdZ6BzkYHwo0Z0b7rxGcEOj0-OKht11MI.mp4
975
- Copy of SnapInsta.to_AQOunhidS10kZDOpHA6A9XGjMiODoeBr80xL6OiobgMdScNwcEqfqYdkv6ZfUT2qeaYpbUQGsunb_0XvGWjnUd7EAW2spOjY52CffKg.mp4
976
- Copy of SnapInsta.to_AQOx8IAHWDOyhg0CHM6zhgfAdJyFXrY_dVIxkhyZTxb3k9s0zxBHmOcQO1CEMO8KsNe1NNZuVweojv1oOCUxzQjC9oP52_PB93lFz0A.mp4
977
- Copy of SnapInsta.to_AQOTkgPUq8D0urBxsk19Is__Jiyq5ftumyFFjMFokXSRnR3uOKqRvCmd1AduHKU3gO0Rtf0NwW4L1jKLvyiPFId43MkHQ4suqoZBXZc.mp4
978
- Copy of SnapInsta.to_AQP0yDwzuY5u4_OEeXAeTvWRCeeTvvVsOoGu5aeoAP963bZJ14i7ghlYbz6SMsyFNi36vt0kdA1vuEGmfwi6Sfb5iiGxrGJHLpO9OTg.mp4
979
- Copy of SnapInsta.to_AQOsnbtSX-bXxGOt-sabN39kuJQfdqr-6KGKzSfodf6xNJssH8-XAKW2uFo88UnghD08L-_8YXC0DPyx16EqPk1nheICtMAy2QJkCIs.mp4
980
- Copy of SnapInsta.to_AQOSWASFvE9nzj-lC1Itcqe5oyW762iWXVZOiyDTtUlJxawNqUSrSIsRkuM4YUpYVtJQl1WXOGe9jDeOGFFn23HdBZWdZyWfgFEOty4.mp4
981
- Copy of SnapInsta.to_AQP9MoVikQxRPirOrSA-f8BSIgZjFIZ_wo8sId5h4TwdgzwUBX5CtWT_YB0b-Igte3JrDWWC9bwHYCXys5GeN8vhcgP4bCpps4xLEFQ.mp4
982
- Copy of SnapInsta.to_AQP5x_-GxQhzWvPVsQoRFzl8UDQRWPO93pzrQV2qvY1Bi6iWTqhe7Cltb_4YFD3u3I0P5ynjKg8yx_uXk14trK-3ppn-vrEkOl0Zm34.mp4
983
- Copy of SnapInsta.to_AQP6S-RQHa_MPiX26XQjpVDFPEueoDwW0GjhCigPeU7OEnWQbM-0vj6g3rtJpC0pJy3apsej0wTZp0SN5J8edCJMNT7F9PAFYc_hW18.mp4
984
- Copy of SnapInsta.to_AQPEGvaRPB83Lv7enXGL2sdBmGOGgsQQtgcqMDAO4GhhX3lJ0Z4RzoUltbd7HMN-Q__fo1moC8hARK_aK9oSN97FUDMJzCqrrOhVVZE.mp4
985
- Copy of SnapInsta.to_AQP1_On-75FfJhQjoXCVt3V7t95P59YPUqmveJLDjOt2c7ISy-80wt7WKVSz2EeAObMRakx8A2kwxeH1afbkdPfHBctujNVG7tEbgo8.mp4
986
- Copy of SnapInsta.to_AQP6QdDL5_4vOzglW5A-NHotiReXbDQachk3JkBT-OHNNZP0pkvsqLoKRf_Uj40LsXL5oiTmABvTYAFghnR_U1OK0WZMWue-0lTHMM8.mp4
987
- Copy of SnapInsta.to_AQPdJWwfU8eUpHNFvFiy5QZeQhWb7Db_k2FoK4YIjLhsRpTj_DPmtupEVpAbTkoHJQR2QwNuoWndb3wdw3bZ3jkZVylawCmn_PNs7RQ.mp4
988
- Copy of SnapInsta.to_AQPaJ4fbqlmrgWZMENe7Isg7FLZNcLMfxZF__B5UK0tclg_D1JNPuI-nc8mg6jaqisPB1sCyKwKccsioU-_hBo2Lpwoq9_iRRy_WLk8.mp4
989
- Copy of SnapInsta.to_AQPA3YzDniupzyaY57IcU_7irEO3JAvGVqpQ7at0_BLFsYQ-G9apE6g9e1lIytLEoVP3um1qX-h43JD7qkO6zqwt4lpTwniDB8gpHrk.mp4
990
- Copy of SnapInsta.to_AQP9gdaQuLVvaNjxVUJ3Ylx-q1eU3bRDugtiP49H9npnnNtteaIVy9ct63yzQ7W3rNvSZCR3ttqDrSfuVpZt3ObmmIV_kGhhuiiYKa0.mp4
991
- Copy of SnapInsta.to_AQPGUHI008VcSK6wV58xvdjv1NdNbwtRWay4OzkO1I2Sdv9JfSNZ191ukwSQmHXZ_vfw9Z_iFiNTA0Kba9HDvnLVC9YMKvqCjXlLJ68.mp4
992
- Copy of SnapInsta.to_AQPJRsAHkSr6xCVJ1L9f5u56T3dxTSijNv8O6HGKo4iYvpRdg1uK3eGSRZSWC5vKiSWzHLU00BGsfOMPvUkfyeetf_2Vy7nhkR3eXlo.mp4
993
- Copy of SnapInsta.to_AQPHYRsdPGnhgb3f225HPoXXah61TJvSkQfCJnuOEPzAo9l9_2m4vDRl8TDw1zHkOqGTk9eyBAVxxKoJcCqzn1xh.mp4
994
- Copy of SnapInsta.to_AQPIAqJLErRwWYVl-kSAZro51oek2fW-bzxabdyHqgg1Sky_PBjUJpKAl5s9MiAHSOsJwA2UfsVYVw9qpXeMunzKaRQPZ3g-N7t1d34.mp4
995
- working.mov
996
- Rooftop View.MOV
997
- Friends.mov
998
- Girl in bed.MOV
999
- Smoking.MOV
1000
- friends.MOV
1001
- Copy of Walking through cars 02.MOV
1002
- laptop.mov
1003
- Copy of SnapInsta.to_AQN2ykIDs-r4B94ZHW00Xk2u29SC6ULDYqc5k-QdFQN6KMtdXFL9bJ8n3x8y-8eOGDt-x4o45xTW2YIao3p4VeJHssz_FdoUQT-_5yA.mp4
1004
- Copy of SnapInsta.to_AQPJV_cYuWeBgeu0QgPawjLqqc79jTt7hIaOO0oIDCvui2Ex8LhnrX_npxRdD9E61SzYsNp1z0OBPHH4Uy8vLqeYQhFRmDO96jdZ-JQ.mp4
1005
- Copy of SnapInsta.to_AQPfC3cEI2ZeGovnvIimFhLElkjti7GIJ5G4TrfAy0UpjkfBaA6ZBdcvtiI2QbD8ykj-wX_G1jBt8wPtdqqJqd5Len_u7W7nH3Oz8ag.mp4
1006
- Copy of SnapInsta.to_AQPhvYHIzJArhkWJzToJWw2k_8FXsgqjZPWcYE9DRjHYpleWbWvWAzx2lJ1dIa2TZs8tNUIuC6TZ4EvyVQPuDctqlQLOdUWRZ-qy3JA.mp4
1007
- Copy of SnapInsta.to_AQPg7d8KL6jpUQ9h22MjfsC1uR_gGdBSf63Iyi72eSMyxJPjNW4RtIW_d-NmOpjvOZsm1Hdisb-I9ONQ65LGegZ-_SSV81IHmjahEzo.mp4
1008
- Copy of SnapInsta.to_AQPhMjuLqhb9OTzDOpcgEEzLSbl1w2oi17klMc1fX7Qf22vpumdh9Atht8M0CeBDZ1aHhW_fOrcpfSvoxhOmndEefDUt717AQIHWYvk.mp4
1009
- Copy of SnapInsta.to_AQPkdCEMgC-eQ3tMdXeCJb-ua1I_IEa1wWM-SX0MPW8J81pmrXNNONkgFlhnv-JGtiQFPEFlusjJZNurMISW6osyG7NB5vWQwdoG0lk.mp4
1010
- Copy of SnapInsta.to_AQPURpssZNgdsC4xOneUOmzDk14rRjFZGxY6-6mFLRNeo5NkmoHXXGk9pXhIHkQGK7ayznYw1MMu9RhVvvXoZmvqzqnpSkH_n0I5Rys.mp4
1011
- Copy of SnapInsta.to_AQPsGi9QwY5zNctKJzD6znNRLrASPbq9pNtehOF1g3L_jRdkADCDF9HBfx15oWYAfqHe_91dXmv2Je5SFZSSib8W1yVtcVOSZqRVues.mp4
1012
- Copy of SnapInsta.to_AQPSI2P4NidOjTXcpjwfr_ROFUtW5Mqmc_CIZ7EB0kKimrUnbdSpju2_vY3CxTgPvo7sjcmSrv2Qi6zZdn2HNeeTqNKqF0tVOKKoQ6g.mp4
1013
- Copy of SnapInsta.to_AQPMQBm8SSx6ZVK54qL5dnyTjruumSE_rSUXhS_Z7DFSV8EYbQWHSokiNGYO7l_j7qGfJf5tY8K5lY8ObyEG5DrdM5s02mxsRH2K8Kw.mp4
1014
- Copy of SnapInsta.to_AQPnyxKiUJYh7wDrsS-7CgSbg75gPnIRKbgG0ZZbj1dhlFAjVx5NZW_xL-OH1pg2q7-55Ar5l2ObZ2zsDPd3UajS-aS3Q8z6OPKjIVQ.mp4
1015
- Copy of SnapInsta.to_AQPOS2bsqrz46e8ABB_3HBEdQdvg1ZFyKG0I4RqPpwtDaBRLH-MUT6UWQvkP9b-MJDqB4qI7A2bZAOyJ3B_tKqwEmdNCIfvGjZW1TrU.mp4
1016
- Copy of SnapInsta.to_AQPQ_nXb4csarb1XxzpAA_0cFG5rJjBpVQsQDKKyyaqWuJezS7B9Tjo-xNJZ5UL7e_UuFuXEdDyDAp1PY9h4Vt82IkH1muaGbFwCXDs.mp4
1017
- Copy of SnapInsta.to_AQPor_9P2JDtXjCq9vraa1yTRuFo1rv1mwC5B4UHeWwhBn4uT3bXYDHP_Tz-tytuf35u0SMqAJmF4HdTYIDIK090hbF21vJUmJZUldc.mp4
1018
- Copy of SnapInsta.to_AQPO641vvUf3B8a8fL9t32vUpWQlQqUokNHmfdwwg3D9iRuW6P6fw3RMwFpIjYc6HiA2ClYoklPdJgMof5tj8JJfyue0QPQAAVVltaU.mp4
1019
- Copy of SnapInsta.to_AQPKSxCPu8x9JEGt-DQ5i3JR7ObxKSmz-YuQpRP4XsPxdQM5o-_digxLfsCNOXsZaBsLtvFDeP4WoV39rO_bLovrn1fMn_emhKIH4LI.mp4
1020
- Copy of SnapInsta.to_AQPXFsDHAfNSFkevjj6G0NZUMoJyCo-yjUU4e9qCNgOK4p506pPRjBtG5V71zMSWWROqLVmfXPy3fPIdUgKathaMGO1-JoXHWmxLV58.mp4
1021
- Copy of SnapInsta.to_AQPV_ooVDa9QcZg1mybQxz57L8zg2S6ws1GwcVuJvd3bSGfZFwORILEws48HgFi3ViGvtFS7HqciD8vVvki8EJGLK_VYnnOWabhaczk.mp4
1022
- Copy of SnapInsta.to_AQPwZXc2V07XyON8A7PKft2kW3Dd9v9LxV62qKx6M9ywGwxHaSj3o4K08zTyAuG4RrDkC0_K4h6im5BHopg78xTBUYgGJvT12dUqi8k.mp4
1023
- Copy of SnapInsta.to_AQPzXTUvG5jQN4B-wXhlvIsbdnrcYxoj5yGB0IsSf0SNGyNg-3Il1EeJpAfYXJdjhJ6HtmkD11Ax4GsqK7lVvL265uK0HGVDkS08jOc.mp4
1024
- Copy of SnapInsta.to_AQPxefolpO59dgEywT9ip9VAN76Z7buu7nkqdbfhFCCVeA3qlK-tYU_fG7ZapjpOohimutcwBE7ylL3Fv_LJIOfCxQtAX_N6DdFYERc.mp4
1025
- Copy of SnapInsta.to_AQPYaWsrr49XOX2Vftwy9p54iohuoXgPp2zM6W8HCH6o-M565QQccsDLSqf82S7co9bsXPYJjBARGx-SuhZ6aQAAD_ag0ZOQ10Orl1k.mp4
1026
- Copy of SnapInsta.to_AQPVOor9gDMCBOGXjHg9jOdEXZ1dviuIV9zDLX3wY-xGCHuKhkLyd0p74Ml4Rkuxt6OK3WlOPim1cKQV__-gYMGch7d39xOaXTh4PB8.mp4
1027
- Copy of SnapInsta.to_AQPwGj8omuFHL6Q8k7waKtlPmSbi8pwEEpKybmlBX2oGdWtKQCrOBylW9CQ7wF5zQfpAW6QdNq2N2pAXiB14lB94PR_lQj4WBO9CVdc.mp4
1028
- Copy of SnapInsta.to_AQPw5jk4ua3oW4DtlUa3olix-qwqWsXRfY3g-CqeYHgc5S1ICnxaMZ2cz4j9-58nVItKnB2u1iDFi9vBLhYmqm96HfqwIRJmoBRQzkc.mp4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
video_analyser/requirements.txt DELETED
@@ -1,15 +0,0 @@
1
- # Google Drive API
2
- google-api-python-client>=2.100.0
3
- google-auth-httplib2>=0.1.1
4
- google-auth-oauthlib>=1.1.0
5
-
6
- # Gemini AI
7
- google-generativeai>=0.3.0
8
-
9
- # Video Processing (only cv2 for duration extraction)
10
- opencv-python-headless>=4.8.0
11
-
12
- # Configuration & Utilities
13
- pyyaml>=6.0
14
- tqdm>=4.66.0
15
- python-dotenv>=1.0.0