Compare commits

...

10 Commits

Author SHA1 Message Date
422aefd3e0 add movies and update the script
Signed-off-by: Bogomil Vasilev <smirky@smirky.net>
2026-02-13 14:00:30 +02:00
b2b8089320 movielist: update to newer UI
Signed-off-by: Bogomil Vasilev <smirky@smirky.net>
2026-01-19 08:36:09 +02:00
3e7dafb4b2 Improve movie list generator, add more movies
Signed-off-by: Bogomil Vasilev <smirky@smirky.net>
2022-12-07 23:36:36 +02:00
4bfc45c100 Make the script more robust, pylint fixes
Signed-off-by: Bogomil Vasilev <smirky@smirky.net>
2022-04-29 09:37:15 +03:00
481fa5886a add usage, improve getopt
Signed-off-by: Bogomil Vasilev <smirky@smirky.net>
2022-02-11 16:33:42 +02:00
37e29989fe add some more scripts
Signed-off-by: Bogomil Vasilev <smirky@smirky.net>
2022-02-11 14:36:10 +02:00
570014c240 some safety and optimizations 2019-09-29 14:59:20 +03:00
db8f4e1479 improve muting wrapper for threads 2019-09-29 14:42:09 +03:00
aac410f1e1 update movie_list script and list 2019-09-29 14:16:45 +03:00
391ae02488 add the movie_list script 2018-08-29 10:32:06 +03:00
11 changed files with 1439 additions and 0 deletions

131
archy-usb-ctl.sh Executable file
View File

@@ -0,0 +1,131 @@
#!/bin/bash
#
# This script is indended to decrypt, mount and chroot in a LUKS+EFI+LVM USB
if [[ $UID != 0 ]]; then
printf "You need root permissions to use this script!\n"
exit 1
fi
usage()
{
echo -e "\nUsage: $(basename "$0") [options] [luks_device]\n\n" \
"Options:\n" \
"\t-m, --mount Mount the LUKS device (Default)\n" \
"\t-u, --umount Unmount the LUKS device\n" \
"\t-n, --nochroot Don't run arch-chroot\n" \
"\t-c, --chroot Run arch-chroot (Default)\n" \
"\t-h, --help Display this message\n"
}
# Mute lvm utilities
export LVM_SUPPRESS_FD_WARNINGS=true
# Some sane defaults
ACTION=mount
RUN_CHROOT=true
USB_LUKS_PART=
# Call getopt to validate the provided input.
if ! options=$(getopt -o chmnu -l chroot,nochroot,mount,umount,help -- "$@"); then
usage
exit 1
fi
eval set -- "$options"
while true; do
case "$1" in
(-m|--mount) ACTION=mount ;;
(-u|--umount) ACTION=umount ;;
(-n|--nochroot) RUN_CHROOT=false ;;
(-c|--chroot) RUN_CHROOT=true ;;
(-h|--help) usage; exit 1;;
(--) shift; break;;
(*) usage; break;;
esac
shift
done
if [ "$#" -gt 1 ]; then
echo "error: Only 1 device argument possible! Got $#: $*"
usage
exit 1
fi
if [ "$1" ]; then
USB_LUKS_PART="$1"
fi
get_archy_usb_dev_path()
{
declare -A dev_map='([by-id]="usb-ADATA*part3" [by-uuid]="bbd2dd10-4209-4879-a1e2-5ee1eff8ff5c")'
for key in "${!dev_map[@]}"; do
dev_path=$(find -L "/dev/disk/${key}" -name "${dev_map[$key]}")
if [ -n "$dev_path" ]; then
echo "${dev_path}"
break
fi
done
}
get_usb_efi_part()
{
echo "/dev/$(lsblk -ndo pkname "$1")2"
}
get_usb_root_part()
{
lsblk -no path "$1" | grep root
}
usb_mount()
{
cryptsetup open "${USB_LUKS_PART}" adatausb || exit $?
vgchange -a y > /dev/null || exit $?
usb_dev_root=$(get_usb_root_part "${USB_LUKS_PART}")
echo -e "Mounting /\t${usb_dev_root}"
mount "${usb_dev_root}" /mnt/usb
exit_code=$?
if [ $exit_code -ne 0 ]; then
cryptsetup close "${USB_LUKS_PART}"
exit $exit_code
fi
echo -e "Mounting /efi\t${USB_DEV_EFI}"
mount "${USB_DEV_EFI}" /mnt/usb/efi
exit_code=$?
if [ $exit_code -ne 0 ]; then
umount /mnt/usb
cryptsetup close "${USB_LUKS_PART}"
exit $exit_code
fi
if [ $RUN_CHROOT == true ]; then
arch-chroot /mnt/usb
fi
}
usb_umount()
{
usb_dev_root=$(get_usb_root_part "${USB_LUKS_PART}")
umount "${USB_DEV_EFI}" "${usb_dev_root}" # || exit $?
vgchange -a n --quiet adata > /dev/null # || exit $?
cryptsetup close adatausb
}
# Auto-detect, in case we don't provide a device
if [ -z "${USB_LUKS_PART}" ]; then
USB_LUKS_PART=$(get_archy_usb_dev_path)
if [ -z "${USB_LUKS_PART}" ]; then
printf "Device not found!\nUse %s </dev/path/to/LUKS_device>\n" "$0"
exit 1
fi
fi
USB_DEV_EFI=$(get_usb_efi_part "${USB_LUKS_PART}")
if [ -n "${usb_def_efi}" ]; then
echo "EFI device not found!"
exit 1
fi
usb_$ACTION

45
btfs.sh Executable file
View File

@@ -0,0 +1,45 @@
#!/bin/sh
function timestamp() {
date +"%Y%m%d%H%M%S"
}
XDG_CACHE_HOME=${XDG_CACHE_HOME:-$HOME/.cache}
mountpoint="$XDG_CACHE_HOME/btfs-$(timestamp)"
[ ! -d "$mountpoint" ] && mkdir "$mountpoint"
[ -z "$1" ] && printf "%s [magnet|*.torrent]\n" "$0" && exit 1
printf "Mounting %s to %s\n" "$1" "$mountpoint"
btfs "$1" "${mountpoint}"
btfs_status=$?
if [ $btfs_status != 0 ]; then
printf "Failed to mount torrent: %s\n" "$1"
exit $btfs_status
fi
while true; do
movie_dir_size=$(du -hs "$mountpoint"|awk '{print $1}')
printf "movie_dir_size: %s\n" "$movie_dir_size"
if [ "$movie_dir_size" != '4.0K' ] && [ "$movie_dir_size" != "0" ]; then
break;
else
sleep 5;
fi
done
printf "Locating the largest file...\n"
movie="$(find "$mountpoint" -type f -exec du -hs '{}' +|sort -rh|grep -vi sample|head -n1| \
awk '{$1=""; print $0}')"
movie=$(echo $movie|xargs)
[ -z "$movie" ] && printf "Could not locate the movie!\n" && exit 1
printf "Playing: %s\n" "$movie"
#smplayer -fullscreen "$movie" 1>/dev/null
#mpv --fs --volume=100 "$movie" # 1>/dev/null
vlc "$movie" 1>/dev/null
printf "Unmounting: %s\n" "$mountpoint"
fusermount -u "$mountpoint"
printf "Removing dir: %s\n" "$mountpoint"
rmdir "$mountpoint"

82
check-ip-seeds.py Executable file
View File

@@ -0,0 +1,82 @@
#!/usr/bin/env python
import sys
import json
import urllib
import urllib.request as urllib
from html.parser import HTMLParser
from termcolor import colored
class MyHTMLParser(HTMLParser):
tbody = False
div = False
a_href = False
timestamp = False
timestamps = []
category = False
category_value = None
def handle_starttag(self, tag, attrs):
if tag == 'tbody':
self.tbody = True
elif tag == 'div' and self.tbody:
self.div = True
elif tag == 'a' and self.tbody and self.div:
self.a_href = True
if tag == 'td' and self.tbody:
for attr in attrs:
if len(attr) == 2 and attr[0] == 'class':
if attr[1] == 'date-column':
self.timestamp = True
elif attr[1] == 'category-column':
self.category = True
def get_category(self):
colored_category = None
if self.category_value == 'Movies':
colored_category = colored(self.category_value, 'cyan', attrs=['bold'])
elif self.category_value == 'Books':
colored_category = colored(self.category_value, 'green', attrs=['bold'])
elif self.category_value == 'XXX':
colored_category = colored(self.category_value, 'magenta', attrs=['bold'])
else:
colored_category = self.category_value
return f'{colored_category}{(10-len(self.category_value)) * " "}'
def store_category(self, data):
self.category_value = data.strip()
self.category = False
def handle_endtag(self, tag):
if tag == 'tbody':
self.tbody = self.div = self.a_href = False
if tag == 'div':
self.div = self.a_href = False
if tag == 'a_href':
self.a_href = False
def handle_data(self, data):
if self.tbody and self.div and self.a_href:
print('\t| '.join(self.timestamps) + '\t| ' + self.get_category() + '| ' + data.strip())
self.a_href = False
self.timestamps = []
elif self.timestamp:
self.timestamps.append(data)
self.timestamp = False
elif self.category:
self.store_category(data)
if len(sys.argv) != 2:
print(f'{sys.argv[0]} <ip_addr>')
sys.exit(1)
url = f'https://iknowwhatyoudownload.com/en/peer/?ip={sys.argv[1]}'
req = urllib.Request(url, headers={'User-Agent' : "Magic Browser"})
fp = urllib.urlopen(req)
mybytes = fp.read()
mystr = mybytes.decode("utf8")
fp.close()
ip = json.loads(urllib.urlopen(f'http://ipinfo.io/{sys.argv[1]}/json').read().decode('utf8'))
print(f'----- IP: {ip["ip"]} ----- Country: {ip["country"]} ----- City: {ip["city"]} -----')
# instantiate the parser and fed it some HTML
parser = MyHTMLParser()
parser.feed(mystr)

13
clean_chroot_build.sh Executable file
View File

@@ -0,0 +1,13 @@
#!/bin/bash
CHROOT=$HOME/chroot
if [ ! -d "$CHROOT" ]; then
mkdir $CHROOT
mkarchroot $CHROOT/root base-devel
# mkarchroot $CHROOT/root base-devel zsh
else
arch-nspawn $CHROOT/root pacman -Syu
#arch-nspawn $CHROOT/root pacman -S --noconfirm
fi
makechrootpkg -c -r $CHROOT -- $@

65
find_adb_device.py Executable file
View File

@@ -0,0 +1,65 @@
#!/usr/bin/env python3
from os import environ
import subprocess
import json
import sys
import re
def main():
adb_cmd = subprocess.Popen(['adb', 'devices', '-l'], stdout=subprocess.PIPE,
stderr=subprocess.PIPE, universal_newlines=True)
adb_cmd.wait()
out, _ = adb_cmd.communicate()
for num, line in enumerate(out.strip().split('\n')):
if line == 'List of devices attached':
devices = out.strip().split('\n')[num+1:]
break
device = None
if len(sys.argv) == 1:
if len(devices) == 1:
device = devices[0].split()[0]
elif len(devices) > 1:
print(f'Please specify a device: {sys.argv[0]} <device>')
print('More than 1 device detected:')
print('\n'.join(devices))
exit(1)
elif len(devices) == 0:
print('No devices available! Perhaps first try: adb connect <device>')
exit(1)
elif len(sys.argv) == 2:
device = sys.argv[1]
if device not in devices:
print(f'Device not in list, connecting to: {device}')
adb_cmd = subprocess.run(['adb', 'connect', device])
else:
print('Unexpected argument count!')
exit(1)
adb_cmd_string = ['adb', '-s', device, 'shell', 'dumpsys', 'location']
print(f'Running: {adb_cmd_string}')
adb_cmd = subprocess.Popen(
adb_cmd_string,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True)
adb_cmd.wait()
out, err = adb_cmd.communicate()
if adb_cmd.returncode != 0:
print("adb failed:\n{err}")
exit(1)
gps = {}
for line in out.split('\n'):
if 'Gnss Location Data' in line:
gps_data = re.sub('.*Gnss Location Data:: ', '', line)
for attr in gps_data.split(','):
gps[attr.split()[0].replace(':', '')] = attr.split()[1]
# url = f'https://www.google.com/maps/place/{gps["LatitudeDegrees"]},{gps["LongitudeDegrees"]}'
url = f'http://www.openstreetmap.org/?mlat={gps["LatitudeDegrees"]}&mlon={gps["LongitudeDegrees"]}&zoom=17'
if environ.get('DISPLAY'):
subprocess.run(['xdg-open', url])
else:
print(url)
main()

2
movie_list/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
index.html
jquery.dataTables.min.js

546
movie_list/gen_movie_list.py Executable file
View File

@@ -0,0 +1,546 @@
#!/usr/bin/env python
"""
1. Import a movie_list txt file
2. Query IMDb for each entry, retrieving actual movie name, rating and genres
3. Generate an HTML table from the IMDb data
4. Store the HTML in index.html
"""
import os
import sys
import time
import threading
from pathlib import Path
import progressbar
from imdb import IMDb
from imdb._exceptions import IMDbParserError, IMDbDataAccessError
class MovieList:
""" Class to generate a movie list HTML table """
def __init__(self, src=None, dst=None):
self.prev_html = []
self.html = """<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Movie List</title>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.24/css/jquery.dataTables.min.css">
<script src="../jquery-3.7.1.min.js"></script>
<script src="https://cdn.datatables.net/1.10.24/js/jquery.dataTables.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: transparent;
height: 100vh;
padding: 0.5rem;
color: #fff;
overflow: hidden;
display: flex;
flex-direction: column;
}
.container {
background: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(10px);
border-radius: 8px;
border: 1px solid rgba(255, 255, 255, 0.1);
padding: 0.75rem;
height: 100%;
overflow: hidden;
display: flex;
flex-direction: column;
}
h1 {
color: #fff;
margin-bottom: 0.75rem;
font-size: 1.5rem;
font-weight: 600;
text-align: center;
}
.dataTables_wrapper {
flex: 1;
overflow: hidden;
display: flex;
flex-direction: column;
}
.dataTables_filter {
margin-bottom: 0.5rem;
text-align: right;
}
.dataTables_filter label {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 0.5rem;
font-size: 0.9rem;
color: rgba(255, 255, 255, 0.9);
}
.dataTables_filter input {
padding: 0.4rem 0.75rem;
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 6px;
background: rgba(0, 0, 0, 0.3);
color: #fff;
font-size: 0.9rem;
width: 250px;
}
.dataTables_filter input::placeholder {
color: rgba(255, 255, 255, 0.5);
}
.dataTables_filter input:focus {
outline: none;
border-color: rgba(255, 255, 255, 0.4);
background: rgba(0, 0, 0, 0.4);
}
.dataTables_info {
padding: 0.5rem 0;
color: rgba(255, 255, 255, 0.6);
font-size: 0.85rem;
}
.dataTables_scroll {
flex: 1;
overflow: hidden;
display: flex;
flex-direction: column;
}
.dataTables_scrollHead {
flex-shrink: 0;
}
.dataTables_scrollBody {
flex: 1;
overflow-y: auto !important;
}
.dataTables_scrollBody::-webkit-scrollbar {
width: 6px;
}
.dataTables_scrollBody::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.05);
}
.dataTables_scrollBody::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.2);
border-radius: 3px;
}
table.dataTable.stripe tbody tr.odd,
table.dataTable.display tbody tr.odd,
table.dataTable tbody tr {
background: transparent !important;
}
table.dataTable.hover tbody tr:hover,
table.dataTable.display tbody tr:hover {
background: rgba(255, 255, 255, 0.1) !important;
}
table.dataTable tbody td {
background: transparent !important;
}
#sortable {
width: 100%;
border-collapse: collapse;
background: transparent;
}
#sortable thead {
background: rgba(0, 0, 0, 0.4);
}
#sortable thead th {
padding: 0.5rem 0.75rem;
text-align: left;
font-weight: 600;
font-size: 0.85rem;
color: rgba(255, 255, 255, 0.9);
cursor: pointer;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
white-space: nowrap;
}
#sortable thead th:hover {
background: rgba(255, 255, 255, 0.05);
}
#sortable thead th.sorting,
#sortable thead th.sorting_asc,
#sortable thead th.sorting_desc {
padding-right: 1.5rem;
position: relative;
}
#sortable thead th.sorting:after {
content: '';
position: absolute;
right: 0.5rem;
top: 50%;
transform: translateY(-50%);
font-size: 0.75rem;
color: rgba(255, 255, 255, 0.5);
}
#sortable thead th.sorting_asc:after {
content: '';
position: absolute;
right: 0.5rem;
top: 50%;
transform: translateY(-50%);
font-size: 0.75rem;
color: rgba(255, 255, 255, 0.9);
}
#sortable thead th.sorting_desc:after {
content: '';
position: absolute;
right: 0.5rem;
top: 50%;
transform: translateY(-50%);
font-size: 0.75rem;
color: rgba(255, 255, 255, 0.9);
}
#sortable tbody tr {
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
transition: background 0.15s ease;
background: transparent !important;
}
#sortable tbody tr:hover {
background: rgba(255, 255, 255, 0.1) !important;
}
#sortable tbody td {
padding: 0.5rem 0.75rem;
font-size: 0.9rem;
color: rgba(255, 255, 255, 0.9);
background: transparent !important;
}
#sortable tbody td:first-child {
color: rgba(255, 255, 255, 0.5);
font-size: 0.85rem;
width: 50px;
}
#sortable tbody td a {
color: rgba(135, 206, 250, 0.9);
text-decoration: none;
}
#sortable tbody td a:hover {
color: rgba(135, 206, 250, 1);
text-decoration: underline;
}
#sortable tbody td p[hidden] {
display: none;
}
#sortable tbody td:nth-child(4) {
color: rgba(255, 193, 7, 0.9);
font-weight: 500;
}
.timestamp {
text-align: center;
margin-top: 0.5rem;
padding-top: 0.5rem;
border-top: 1px solid rgba(255, 255, 255, 0.1);
color: rgba(255, 255, 255, 0.5);
font-size: 0.8rem;
}
</style>
<script>
$(document).ready(function(){
$('#sortable').DataTable({
"paging": false,
"info": true,
"searching": true,
"ordering": true,
"order": [[0, "asc"]],
"scrollY": "calc(100vh - 200px)",
"scrollCollapse": true,
"language": {
"search": "Search:",
"info": "Showing _TOTAL_ movies",
"infoEmpty": "No movies",
"infoFiltered": "(filtered from _MAX_)"
}
});
});
</script>
</head>
<body>
<base target="_parent" />
<div class="container">
<h1>🎬 My Movie Collection</h1>
<table id="sortable" class="sortable">
<thead>
<tr>
<th>#</th>
<th>Title</th>
<th>Year</th>
<th>Rating</th>
<th>Genre</th>
<th>Status</th>
</tr>
</thead>
<tbody>"""
self.src = src
self.dst = Path(dst) if dst else Path(os.path.dirname(sys.argv[0])) / 'index.html'
self.movie_list = []
self.threads = []
self.read_prev_output()
self.html_table = None
def _worker(self, arg, index):
# Scan IMDb for a given movie and append it to the html
# This collects rating, genres, official name and a hyperlink
imdb = IMDb()
first_run = True
while True:
if not first_run:
time.sleep(10)
else:
first_run = False
try:
query = imdb.search_movie(f'{arg["title"]} {arg["year"]}')
break
except IMDbDataAccessError as imdb_data_exc:
exc = str(imdb_data_exc)
if '503' in exc:
sys.stderr.write('503 - Service Unavailable, retrying...')
elif '403' in exc:
sys.stderr.write('403 - Forbidden, retrying...\n')
query = []
time.sleep(10)
except IMDbParserError as imdb_parser_exc:
query = []
break
except Exception as exc:
time.sleep(10)
movie = None
for entry in query:
try:
imdb.update(entry)
except Exception as e:
sys.stderr.write('update err')
# in case any of these keys is missing in the query, continue
if not all(key in entry.keys() for key in ['kind', 'year', 'title']):
continue
if arg['status'] == 'DONE' and 'rating' not in entry.keys():
continue
# Try to eliminate episode results
if [i for i in entry.keys() if 'episode' in i.lower()] or (
'episode' in entry['title'].lower() and \
'episode' not in arg['title'].lower()):
continue
if entry['kind'].lower() == arg['kind'].lower():
movie = entry
break
if not movie:
movie = {
'title': arg['title'],
'kind': arg['kind'],
'year': arg['year'],
'dummy': None
}
if 'genres' not in movie.keys():
movie['genres'] = ['N/A']
if 'rating' not in movie.keys():
movie['rating'] = 'N/A'
html_title_td = movie['title'] if 'dummy' in movie.keys() else \
f'<a href="https://www.imdb.com/title/tt{movie.movieID}" target="_blank">{movie["title"]}</a>'
self.html_table[index] = (
f'\n <tr>'
f'<td data-label="#">{index + 1}</td>'
f'<td data-label="Title"><p hidden>{arg["title"]}</p>{html_title_td}</td>'
f'<td data-label="Year">{movie["year"]}</td>'
f'<td data-label="Rating" align="center">{movie["rating"]}</td>'
f'<td data-label="Genre">{", ".join(movie["genres"])}</td>'
f'<td data-label="Status" align="center">{arg["status"]}</td>'
f'</tr>'
)
def gen(self):
""" Generate an HTML list based on input, using a threaded worker """
if not self.src:
self.src = Path(os.path.dirname(sys.argv[0])) / 'movie_list'
else:
self.src = Path(self.src)
if not self.src.exists():
sys.stderr.write(f'error: input does not exist - {self.src}\n')
return False
self.movie_list = {}
# Open the movie list & split the columns
with open(self.src, 'r', encoding='utf-8') as fp_handle:
mlist_raw = fp_handle.read()
for raw_line in mlist_raw.splitlines():
# In case the line is empty
if not raw_line:
continue
self.movie_list.update({
len(self.movie_list): {
'title': raw_line[0:next((i for i, ch in enumerate(raw_line) if ch in {'<', '('}), None) - 1],
'kind': raw_line[raw_line.find('<')+1:raw_line.rfind('>')+1].strip('<>') or 'movie',
'year': raw_line[raw_line.find('(')+1:raw_line.find(')')],
'status': raw_line[raw_line.find('[')+1:raw_line.find(']')],
}
})
self.html_table = [None] * len(self.movie_list)
# Progress bar
pbar = progressbar.ProgressBar(max_value=len(self.movie_list))
for idx, movie in self.movie_list.items():
# More precise matching - look for the hidden <p> tag with exact title
match = [html_row for html_row in self.prev_html
if f'<p hidden>{movie["title"]}</p>' in html_row
and 'N/A' not in html_row]
if match:
# Update the index and status from the cached row
match_str = match[0]
# Replace the status (* -> DONE or vice versa)
match_str = match_str.replace('*', movie['status']).replace('DONE', movie['status'])
# Update the index number
if '<td data-label="#">' in match_str:
# Extract everything after the index cell
after_index = match_str.split('</td>', 1)[1] if '</td>' in match_str else match_str
self.html_table[idx] = f'\n <tr><td data-label="#">{idx + 1}</td>{after_index}'
else:
self.html_table[idx] = match_str
pbar.increment()
else:
thread = threading.Thread(target=self._worker, args=(movie, idx))
self.threads.append(thread)
max_threads = 10
while self.threads:
threads_alive = self.get_alive_threads()
threads_to_be_started = [i for i in self.threads if i not in threads_alive]
for idx in range(max_threads if max_threads < len(threads_to_be_started) else len(threads_to_be_started)):
threads_to_be_started[idx].start()
pbar.increment()
time.sleep(2)
time.sleep(2)
self.delete_finished_threads()
self.html += ''.join(self.html_table)
# Deduplicate entries before writing
num_entries = self.deduplicate_html()
print(f"\nDeduplicated to {num_entries} unique entries")
self.html = self.html.split('</tbody>')[0] # Remove everything after tbody if it exists
self.html += ''.join(self.html_table)
return True
def delete_finished_threads(self):
for idx, thread in enumerate(self.threads):
if not thread.is_alive() and thread._started.is_set():
thread.join()
self.threads[idx] = None
self.threads = list(filter(lambda a: a is not None, self.threads))
def get_alive_threads(self):
threads = []
for thread in self.threads:
if thread.is_alive() or thread._started.is_set():
threads.append(thread)
return threads
def write(self, dst=None):
""" Write the HTML list to index.html """
out_path = dst if dst else self.dst
timestamp = time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime())
self.html += f'''
</tbody>
</table>
</div>
<div class="timestamp">Generated {timestamp} UTC</div>
</body>
</html>'''
with open(out_path, 'wb') as fp_handle:
fp_handle.write(self.html.encode('utf8'))
def read_prev_output(self):
""" Import a previous HTML table """
if self.dst.exists():
with open(self.dst, 'rb') as fp_handle:
self.prev_html = fp_handle.read().decode('utf8').split('\n')
def deduplicate_html(self):
""" Remove duplicate entries from html_table based on movie titles """
seen_titles = set()
deduplicated = []
for idx, row in enumerate(self.html_table):
if row is None:
continue
# Extract the hidden title from the row
if '<p hidden>' in row and '</p>' in row:
start = row.find('<p hidden>') + 10
end = row.find('</p>', start)
title = row[start:end]
if title not in seen_titles:
seen_titles.add(title)
deduplicated.append(row)
else:
# Skip duplicate
continue
else:
# If we can't find the hidden title, keep the row anyway
deduplicated.append(row)
# Update html_table with deduplicated content
self.html_table = deduplicated
return len(self.html_table)
def main():
""" Default run """
src = dst = None
if len(sys.argv) > 3:
sys.stderr.write(f'error: max 2 variables, {len(sys.argv)-1} given!\n')
sys.exit(1)
if len(sys.argv) > 1:
src = sys.argv[1]
if len(sys.argv) == 3:
dst = sys.argv[2]
mlist = MovieList(src=src, dst=dst)
if mlist.gen():
mlist.write(dst=dst)
if __name__ == "__main__":
main()

468
movie_list/movie_list Normal file
View File

@@ -0,0 +1,468 @@
Edge of Tomorrow (2014) [DONE]
Third Person (2013) [DONE]
Outbreak (1995) [DONE]
Hanna (2011) [DONE]
School Ties (1992) [DONE]
I Am Legend (2007) [DONE]
Life Is Beautiful (1997) [DONE]
Hitch (2005) [DONE]
The Curious Case of Benjamin Button (2008) [DONE]
The Pursuit of Happyness (2006) [DONE]
Pay It Forward (2000) [DONE]
American Gangster (2007) [DONE]
Mr. Nobody (2009) [DONE]
The Jacket (2005) [DONE]
Two Night Stand (2014) [DONE]
Chef (2014) [DONE]
Mary and Max (2009) [DONE]
Lucky Number Slevin (2006) [DONE]
The Road (2009) [DONE]
Magnolia (1999) [DONE]
The Monster (1994) [DONE]
The Tiger and the Snow (2005) [DONE]
Lucy (2014) [DONE]
End of Watch (2012) [DONE]
The Prestige (2006) [DONE]
Léon (1994) [DONE]
Reservoir Dogs (1992) [DONE]
Ratatouille (2007) [DONE]
Cloud Atlas (2012) [DONE]
Office Space (1999) [DONE]
Life of Pi (2012) [DONE]
The Intouchables (2011) [DONE]
The Pianist (2002) [DONE]
Stand by Me (1986) [DONE]
Coach Carter (2005) [DONE]
Law Abiding Citizen (2009) [DONE]
The Butterfly Effect (2004) [DONE]
Devil's Advocate (1997) [DONE]
Braveheart (1995) [DONE]
Body of Lies (2008) [DONE]
Midnight in Paris (2011) [DONE]
Ghost Town (2008) [DONE]
RED (2010) [DONE]
The Fountain (2006) [DONE]
The Boy in the Striped Pajamas (2008) [DONE]
Ip Man (2008) [DONE]
The Bourne Identity (2002) [DONE]
The Bourne Supremacy (2004) [DONE]
The Bourne Ultimatum (2007) [DONE]
Heat (1995) [DONE]
A Beautiful Mind (2001) [DONE]
Sin City (2005) [DONE]
Fargo (1996) [DONE]
Snatch (2000) [DONE]
Blood Diamond (2006) [DONE]
The Departed (2006) [DONE]
Forrest Gump (1994) [DONE]
Schindler's List (1993) [DONE]
The Shawshank Redemption (1994) [DONE]
Limitless (2011) [DONE]
Seven Pounds (2008) [DONE]
I, Robot (2004) [DONE]
The Usual Suspects (1995) [DONE]
The Sixth Sense (1999) [DONE]
The Mechanic (2011) [DONE]
The King's Speech (2010) [DONE]
The Green Mile (1999) [DONE]
The Game (1997) [DONE]
The Edge (1997) [DONE]
Taken (2008) [DONE]
Taken 2 (2012) [DONE]
Taken 3 (2014) [DONE]
Shutter Island (2010) [DONE]
Scent of a Woman (1992) [DONE]
Papillon (1973) [DONE]
Oliver Twist (2005) [DONE]
Memento (2000) [DONE]
Lock, Stock and Two Smoking Barrels (1998) [DONE]
The Crimson Rivers (2000) [DONE]
Inception (2010) [DONE]
Headhunters (2011) [DONE]
Fearless (2006) [DONE]
Unleashed (2005) [DONE]
Catch Me If You Can (2002) [DONE]
Sweeney Todd: The Demon Barber of Fleet Street (2007) [DONE]
King of California (2007) [DONE]
Pulp Fiction (1994) [DONE]
The Godfather (1972) [DONE]
The Matrix (1999) [DONE]
The Matrix Reloaded (2003) [DONE]
The Matrix Revolutions (2003) [DONE]
Se7en (1995) [DONE]
The Rock (1996) [DONE]
Saving Private Ryan (1998) [DONE]
A Time to Kill (1996) [DONE]
The Hobbit: An Unexpected Journey (2012) [DONE]
The Hobbit: The Desolation of Smaug (2013) [DONE]
The Hobbit: The Battle of the Five Armies (2014) [DONE]
The Lord of the Rings: The Fellowship of the Ring (2001) [DONE]
The Lord of the Rings: The Two Towers (2002) [DONE]
The Lord of the Rings: The Return of the King (2003) [DONE]
The Machinist (2004) [DONE]
The Imaginarium of Doctor Parnassus (2009) [DONE]
Transcendence (2014) [DONE]
Dark Shadows (2012) [DONE]
The Rum Diary (2011) [DONE]
Pirates of the Caribbean: The Curse of the Black Pearl (2003) [DONE]
Pirates of the Caribbean: Dead Man's Chest (2006) [DONE]
Pirates of the Caribbean: At World's End (2007) [DONE]
Pirates of the Caribbean: On Stranger Tides (2011) [DONE]
Pirates of the Caribbean: Dead Men Tell No Tales (2017) [*]
Star Wars: Episode I - The Phantom Menace (1999) [DONE]
Star Wars: Episode II - Attack of the Clones (2002) [DONE]
Star Wars: Episode III - Revenge of the Sith (2005) [DONE]
Star Wars: Episode IV - A New Hope (1977) [DONE]
Star Wars: Episode V - The Empire Strikes Back (1980) [DONE]
Star Wars: Episode VI - Return of the Jedi (1983) [DONE]
Star Wars: Episode VII: The Force Awakens (2015) [DONE]
Rogue One: A Star Wars Story (2016) [DONE]
Men in Black (1997) [DONE]
Men in Black II (2002) [DONE]
Men in Black 3 (2012) [DONE]
Hancock (2008) [DONE]
Silver Linings Playbook (2012) [DONE]
Meet the Parents (2000) [DONE]
Meet the Fockers (2004) [DONE]
Showtime (2002) [DONE]
Analyze This (1999) [DONE]
Jackie Brown (1997) [DONE]
The Untouchables (1987) [DONE]
Rango (2011) [DONE]
Lord of War (2005) [DONE]
Alice in Wonderland (2010) [DONE]
Public Enemies (2009) [DONE]
Corpse Bride (2005) [DONE]
Finding Neverland (2004) [DONE]
Secret Window (2004) [DONE]
Once Upon a Time in Mexico (2003) [DONE]
Blow (2001) [DONE]
Chocolat (2000) [DONE]
Sleepy Hollow (1999) [DONE]
The Man Who Cried (2000) [DONE]
The Ninth Gate (1999) [DONE]
Donnie Brasco (1997) [DONE]
Platoon (1986) [DONE]
One Flew Over the Cuckoo's Nest (1975) [DONE]
Goodfellas (1990) [DONE]
City of God (2002) [DONE]
Once Upon a Time in the West (1968) [DONE]
Gladiator (2000) [DONE]
300 (2006) [DONE]
Django Unchained (2012) [DONE]
American Beauty (1999) [DONE]
Black Swan (2010) [DONE]
WALL·E (2008) [DONE]
Amélie (2001) [DONE]
Eternal Sunshine of the Spotless Mind (2004) [DONE]
Inglourious Basterds (2009) [DONE]
Unforgiven (1992) [DONE]
Scarface (1983) [DONE]
Die Hard (1988) [DONE]
Die Hard 2 (1990) [DONE]
Die Hard with a Vengeance (1995) [DONE]
Live Free or Die Hard (2007) [DONE]
A Good Day to Die Hard (2013) [DONE]
Die Another Day (2002) [DONE]
A Long Way Down (2014) [DONE]
Remember Me (2010) [DONE]
Automata (2014) [DONE]
The World Is Not Enough (1999) [DONE]
The Thomas Crown Affair (1999) [DONE]
Tomorrow Never Dies (1997) [DONE]
GoldenEye (1995) [DONE]
Mrs. Doubtfire (1993) [DONE]
Dracula Untold (2014) [DONE]
Interstellar (2014) [DONE]
Fury (2014) [DONE]
The Maze Runner (2014) [DONE]
Ender's Game (2013) [DONE]
Stonehearst Asylum (2014) [DONE]
Predestination (2014) [DONE]
Jurassic World (2015) [DONE]
Pan (2015) [DONE]
The Cobbler (2014) [DONE]
The Conjuring (2013) [DONE]
Starred Up (2013) [DONE]
In Order of Disappearance (2014) [DONE]
The Imitation Game (2014) [DONE]
Begin Again (2013) [DONE]
A Walk Among the Tombstones (2014) [DONE]
Detachment (2011) [DONE]
Identity (2003) [DONE]
Force Majeure (2014) [DONE]
Gone Girl (2014) [DONE]
The Equalizer (2014) [DONE]
The Equalizer 2 (2018) [DONE]
Desert Flower (2009) [DONE]
I Origins (2014) [DONE]
Whiplash (2014) [DONE]
Redirected (2014) [DONE]
Nightcrawler (2014) [DONE]
The Judge (2014) [DONE]
I Am Sam (2001) [DONE]
The Bank Job (2008) [DONE]
Child 44 (2015) [DONE]
Big Hero 6 (2014) [DONE]
Rise of the Guardians (2012) [DONE]
De vrais mensonges (2010) [DONE]
Unbroken (2014) [DONE]
Kingsman: The Secret Service (2014) [DONE]
Kidnapping Mr. Heineken (2015) [DONE]
The Blind Side (2009) [DONE]
Leviafan (2014) [DONE]
The Rewrite (2014) [DONE]
Focus (2015) [DONE]
The Wedding Ringer (2015) [DONE]
The Forger (2014) [DONE]
Project Almanac (2014) [DONE]
Run All Night (2015) [DONE]
The Age of Adaline (2015) [DONE]
Tomorrowland (2015) [DONE]
Before We Go (2014) [DONE]
John Doe: Vigilante (2014) [DONE]
Blown Away (1994) [DONE]
The Man from U.N.C.L.E. (2016) [DONE]
Southpaw (2015) [DONE]
Concussion (2015) [DONE]
Spy (2015) [DONE]
Gravity (2013) [DONE]
The Revenant (2015) [DONE]
Ex Machina (2014) [DONE]
The Martian (2015) [DONE]
Shelter (2014) [DONE]
Criminal Activities (2015) [DONE]
The Walk (2015) [DONE]
The Finest Hours (2016) [DONE]
Gods of Egypt (2016) [DONE]
The Terminal (2004) [DONE]
In The Heart of the Sea (2015) [DONE]
Secret in Their Eyes (2015) [DONE]
Zootopia (2016) [DONE]
Ip Man 3 (2015) [DONE]
Miss Peregrine's Home for Peculiar Children (2016) [DONE]
Deadpool (2016) [DONE]
Assassin's Creed (2016) [DONE]
John Q (2002) [DONE]
Now You See Me (2013) [DONE]
Up (2009) [DONE]
The Shallows (2016) [DONE]
Boychoir (2014) [DONE]
Same Kind of Different as Me (2017) [DONE]
Allied (2016) [DONE]
Mad Max: Fury Road (2015) [DONE]
Blood Father (2016) [DONE]
Citizenfour (2014) [DONE]
Kon-Tiki (2012) [DONE]
Our Kind of Traitor (2016) [DONE]
Inferno (2016) [DONE]
En man som heter Ove (2015) [DONE]
Ghost In The Shell (2017) [DONE]
War Dogs (2016) [DONE]
Equals (2015) [DONE]
The Outsiders (1983) [DONE]
Fantastic Beasts and Where to Find Them (2016) [DONE]
Joy (2015) [DONE]
Coherence (2013) [DONE]
The Elephant Man (1980) [DONE]
The Jungle Book (2016) [DONE]
The Girl on the Train (2016) [DONE]
Passengers (2016) [DONE]
Arrival (2016) [DONE]
Rust and Bone (2012) [DONE]
Jack Reacher (2012) [DONE]
At The End of The Tunnel (2016) [DONE]
The Town (2010) [DONE]
The Giver (2014) [DONE]
John Wick (2014) [DONE]
John Wick: Chapter 2 (2017) [DONE]
Lost in Translation (2003) [DONE]
The Graduate (1967) [DONE]
Logan (2017) [DONE]
K-PAX (2001) [DONE]
The Best Offer (2013) [DONE]
Fences (2016) [DONE]
Inside I'm Dancing (2004) [DONE]
My Name Is Khan (2010) [DONE]
Life (2017) [DONE]
Gifted (2017) [DONE]
Spider-Man: Homecoming (2017) [DONE]
Rememory (2017) [DONE]
Wristcutters: A Love Story (2006) [DONE]
Bohemian Rhapsody (2018) [DONE]
The Hitmans's Bodyguard (2017) [DONE]
Danny Collins (2015) [DONE]
Annabelle: Creation (2017) [DONE]
Secrets of State (2008) [*]
Sleepers (1996) [DONE]
The Disaster Artist (2017) [DONE]
Slumdog Millionaire (2008) [DONE]
Ready Player One (2018) [DONE]
Dorian Gray (2009) [DONE]
Serenity (2019) [DONE]
Death Wish (2018) [DONE]
Loving Pablo (2017) [DONE]
Get Out (2017) [DONE]
Orphan (2009) [DONE]
Upgrade (2018) [DONE]
Dangal (2016) [DONE]
BlacKkKlansman (2018) [DONE]
The Shape of Water (2017) [DONE]
What Women Want (2000) [DONE]
What's Love Got to Do with It (1993) [DONE]
Spotlight (2015) [DONE]
First Man (2018) [DONE]
Halloween (2018) [*]
Catch-22 (1970) [*]
The Nun (2018) [DONE]
Into The White (2012) [DONE]
Trash (2014) [DONE]
First Man (2018) [DONE]
Full Metal Jacket (1987) [*]
Roman J. Israel, Esq. (2017) [DONE]
Venom (2018) [DONE]
Moby Dick (1998) <TV Mini Series> [DONE]
The Help (2011) [DONE]
Hidden Figures (2016) [DONE]
The Guilty (2018) [DONE]
Green Book (2018) [DONE]
The Road Within (2014) [DONE]
King of Thieves (2018) [DONE]
The House That Jack Built (2018) [DONE]
Captain Marvel (2019) [DONE]
Miss Sloane (2016) [DONE]
The Bra (2018) [DONE]
The Mule (2018) [DONE]
Escape Room (2019) [DONE]
Man on a Ledge (2012) [DONE]
The Highwaymen (2019) [DONE]
Cold War (2018) [*]
Cold Pursuit (2019) [DONE]
Ad Astra (2019) [DONE]
Storm Boy (2019) [DONE]
Extremely Wicked, Shockingly Evil and Vile (2019) [DONE]
The Post (2017) [DONE]
Angel Has Fallen (2019) [DONE]
The Irishman (2019) [DONE]
The Nightingale (2018) [DONE]
Code 8 (2019) [DONE]
Togo (2019) [*]
Knives Out (2019) [DONE]
Children of Men (2006) [DONE]
Bogowie (2014) [DONE]
Ip Man 4 (2019) [DONE]
The Collini Case (2019) [DONE]
The Traitor (2019) [DONE]
Motherless Brooklyn (2019) [DONE]
The Banker (2020) [DONE]
The Gentlemen (2019) [DONE]
The Platform (2019) [DONE]
Radioactive (2019) [DONE]
Radium Girls (2018) [DONE]
The Stanford Prison Experiment (2015) [DONE]
Red Dragon (2002) [DONE]
The Silence of the Lambs (1991) [DONE]
Hannibal (2001) [DONE]
Parasite (2019) [DONE]
Rounds (2019) [DONE]
Murder On The Orient Express (2017) [DONE]
Pinocchio (2019) [DONE]
The Professor and the Madman (2019) [DONE]
Andhadhun (2018) [DONE]
Archive (2020) [DONE]
The Silencing (2020) [DONE]
Underwater (2020) [DONE]
Love and Monsters (2020) [DONE]
Marshall (2017) [DONE]
The Invisible Man (2020) [DONE]
Greenland (2020) [DONE]
Lean on Me (1989) [DONE]
Let Him Go (2020) [DONE]
Super Lopez (2018) [DONE]
Midnight Special (2016) [DONE]
Another Round (2020) [DONE]
Promising Young Woman (2020) [DONE]
La Daronne (2020) [DONE]
Tangerines (2013) [DONE]
Bridge of Spies (2015) [DONE]
Glass (2019) [DONE]
The Hunt (2020) [DONE]
The Father (2020) [DONE]
The Dry (2020) [DONE]
The Little Things (2021) [DONE]
Arrival (2016) [DONE]
Duel (1971) [DONE]
The Courier (2020) [DONE]
The Bone Collector (1999) [DONE]
Oslo <TV Movie> (2021) [DONE]
Wrath of Man (2021) [DONE]
Nobody (2021) [DONE]
Голата истина за група Жигули (2021) [DONE]
Wild Tales (2014) [DONE]
My Son (2021) [DONE]
The Guilty (2021) [DONE]
Finch (2021) [DONE]
Last Night in Soho (2021) [DONE]
Deepwater Horizon (2016) [DONE]
Boss Level (2020) [DONE]
Boite Noire (2021) [DONE]
Fresh (2022) [DONE]
Death on the Nile (2022) [DONE]
All Quiet on the Western Front (2022) [DONE]
Triangle of Sadness (2022) [DONE]
Jerry and Marge Go Large (2022) [DONE]
Fighting With My Family (2019) [DONE]
The Woman King (2022) [DONE]
Capernaum (2018) [DONE]
The Menu (2022) [DONE]
Rye Lane (2023) [DONE]
Sisu (2022) [DONE]
Zodiac (2007) [DONE]
Legend (2015) [DONE]
Luther The Fallen Sun (2023) [DONE]
Mr. Morgan's Last Love (2013) [DONE]
Sweet November (2001) [DONE]
The Debt (2010) [DONE]
Don Jon (2013) [DONE]
Nefarious (2023) [DONE]
Oppenhaimer (2023) [DONE]
No Hard Feelings (2023) [DONE]
Powder (1995) [DONE]
The Life of David Gale (2003) [DONE]
Crazy, Stupid, Love. (2011) [DONE]
Before We Go (2014) [DONE]
The Banshees of Inisherin (2022) [DONE]
Three Thousand Years of Longing (2022) [DONE]
Hardcore Henry (2015) [DONE]
Chalga (2023) [DONE]
Dumb Money (2023) [DONE]
Society of the Snow (2023) [DONE]
The Aviator's Wife (1981) [*]
Past Lives (2023) [DONE]
Comandante (2023) [DONE]
The Lost Boys (1987) [DONE]
Blade Runner (2049) [DONE]
The Lunchbox (2013) [DONE]
Half Nelson (2006) [DONE]
The Dirt (2019) [DONE]
Manchester by the Sea (2016) [DONE]
The Perks of Being a Wallflower (2012) [DONE]
The Ministry of Ungentlemanly Warfare (2024) [DONE]
Wings of Desire (1987) [*]
Cadillac Records (2008) [DONE]
The Substance (2024) [DONE]
Don't Look Up (2021) [DONE]
La Vie en Rose (2007) [DONE]
The Peanut Butter Falcon (2019) [DONE]
One Battle After Another (2025) [DONE]
The Master and Margarita (2024) [DONE]
Woman at War (2018) [DONE]
Good Fortune (2025) [DONE]
The Age of Disclosure (2025) [*]
Evil Does Not Exist (2023) [DONE]
Black Cat, White Cat (1998) [DONE]
Nuremberg (2025) [DONE]
Poor Things (2023) [*]
The French Dispatch (2021) [DONE]
Sentimental Value (2025) [DONE]
28 Years Later: The Bone Temple (2026) [*]

View File

@@ -0,0 +1,11 @@
(function() {
var si = document.querySelector(".scroll-indicator");
var onscroll = function() {
var max = document.body.scrollHeight - window.innerHeight;
si.style.display = "block";
si.style.width = (window.pageYOffset / max * 100) + "%";
};
window.onscroll = onscroll;
onscroll();
})();

41
movie_list/style.css Normal file
View File

@@ -0,0 +1,41 @@
.scroll-indicator {
display: none;
position: fixed;
top: 0;
left: 0;
width: 50%;
height: 10px;
background: #C33;
}
th {
background-color: #4CAF50;
cursor:pointer;
text-align: center;
}
th, td {
padding: 1px;
border-bottom: 1px solid #ddd;
font-family: arial, sans-serif;
font-size: 12px;
}
body {
background-color: #3D3635;
font-family: arial, sans-serif;
color: white;
font-size: 12px;
}
td {
color: #ffffff;
background-color: #3D3635;
}
a:link, a:visited {
background-color: #3D3635;
color: white;
text-decoration: none;
display: inline-block;
}

35
proton.sh Executable file
View File

@@ -0,0 +1,35 @@
#!/usr/bin/zsh
# Run Windows programs through Proton from Steams installation, without
# starting the main Steam client.
#
# Create a directory for the Proton environment to run in. As an example make
# a folder "proton" in your home and point to it in the "env_dir" variable.
# Then look in the "steamapps/common/" folder in your Steam installation for
# available Proton versions to use. Pick one and point the variable
# "install_dir" to that directory. Optionally install this script in a
# directory that is in your $PATH, so you can run it easily from any place.
#
# Usage:
# proton ./program.exe
# Path to the directory where the actual Proton from Steam is installed in.
# install_dir="$HOME/.steam/steam/steamapps/common/Proton - Experimental"
install_dir="$HOME/gamez/steam-linux/steamapps/common/Proton - Experimental"
# Steam / Client path
export STEAM_COMPAT_CLIENT_INSTALL_PATH="$HOME/.steam/steam"
# Data folder for Proton/WINE environment. Folder must exist.
env_dir=$HOME/proton
# Proton modes to run
# run = start target app
# waitforexitandrun = wait for wineserver to shut down
# getcompatpath = linux -> windows path
# getnativepath = windows -> linux path
mode=run
# EXECUTE
export STEAM_COMPAT_DATA_PATH=$env_dir
"$install_dir/proton" $mode $*