ChandimaPrabath commited on
Commit
feb5ce7
·
1 Parent(s): d451d18
app.py CHANGED
@@ -1,14 +1,9 @@
1
- from flask import Flask, jsonify, render_template, request, Response
2
  import os
3
- import json
4
- import re
5
  import urllib.parse
6
- from threading import Thread
7
- import uuid
8
- from video import ffmpeg_stream
9
- from indexer import indexer
10
  from dotenv import load_dotenv
11
- from tvdb import fetch_and_cache_json
12
 
13
  load_dotenv()
14
  INDEX_FILE = os.getenv("INDEX_FILE")
@@ -16,50 +11,28 @@ TOKEN = os.getenv("TOKEN")
16
  REPO = os.getenv("REPO")
17
  CACHE_DIR = os.getenv("CACHE_DIR")
18
 
 
19
  if not os.path.exists(CACHE_DIR):
20
  os.makedirs(CACHE_DIR)
21
 
22
- indexer()
23
-
24
  if not os.path.exists(INDEX_FILE):
25
  raise FileNotFoundError(f"{INDEX_FILE} not found. Please make sure the file exists.")
26
 
27
  with open(INDEX_FILE, 'r') as f:
28
  file_structure = json.load(f)
29
 
30
- def prefetch_metadata():
31
- for item in file_structure:
32
- if 'contents' in item:
33
- for sub_item in item['contents']:
34
- original_title = sub_item['path'].split('/')[-1]
35
- media_type = 'series' if item['path'].startswith('tv') else 'movie'
36
- title, year = extract_title_and_year(original_title)
37
-
38
- fetch_and_cache_json(original_title, title, media_type, year)
39
-
40
- def extract_title_and_year(original_title):
41
- match = re.search(r'\((\d{4})\)', original_title)
42
- if match:
43
- year_str = match.group(1)
44
- if year_str.isdigit() and len(year_str) == 4:
45
- title = original_title[:match.start()].strip()
46
- return title, int(year_str)
47
- else:
48
- parts = original_title.rsplit(' ', 1)
49
- if len(parts) > 1 and parts[-1].isdigit() and len(parts[-1]) == 4:
50
- return parts[0].strip(), int(parts[-1])
51
- return original_title, None
52
 
53
  def get_film_file_path(title):
54
  decoded_title = urllib.parse.unquote(title)
55
  normalized_title = decoded_title.split(' (')[0].strip()
56
-
57
  for item in file_structure:
58
  if item['path'].startswith('films'):
59
  for sub_item in item['contents']:
60
  sub_item_title = sub_item['path'].split('/')[-1]
61
  normalized_sub_item_title = sub_item_title.split(' (')[0].strip()
62
-
63
  if normalized_title == normalized_sub_item_title:
64
  for file in sub_item['contents']:
65
  if file['type'] == 'file':
@@ -85,47 +58,19 @@ def get_tv_show_seasons(title):
85
  return seasons
86
  return []
87
 
88
- def start_prefetching():
89
- prefetch_metadata()
90
-
91
- def generate(file_url):
92
- # Generate a unique stream ID
93
- stream_id = str(uuid.uuid4())
94
- output_dir = os.path.join(CACHE_DIR, "stream", stream_id)
95
-
96
- # Ensure output directory exists
97
- if not os.path.exists(output_dir):
98
- os.makedirs(output_dir)
99
-
100
- # Set up HLS streaming
101
- token = TOKEN
102
- output_path = ffmpeg_stream(file_url, token, output_dir=output_dir, stream_id=stream_id)
103
-
104
- if output_path:
105
- return stream_id
106
- return None
107
-
108
- thread = Thread(target=start_prefetching)
109
- thread.daemon = True
110
- thread.start()
111
-
112
- app = Flask(__name__)
113
 
114
  @app.route('/')
115
  def home():
116
  return render_template('index.html')
117
 
118
- @app.route('/tvshow_player')
119
- def tvshow_player():
120
- return render_template('player.html')
121
-
122
- @app.route('/film_player')
123
- def film_player():
124
- title = request.args.get('title')
125
- if not title:
126
- return "No film title provided", 400
127
- return render_template('film_player.html', title=urllib.parse.unquote(title))
128
-
129
  @app.route('/films')
130
  def films():
131
  return render_template('films.html')
@@ -201,31 +146,32 @@ def get_metadata():
201
  def film_details(title):
202
  return render_template('film_details_page.html', title=title)
203
 
204
- @app.route('/api/stream')
205
- def stream_video():
206
- file_path = request.args.get('path')
207
- if not file_path:
208
- return "File path not provided", 400
 
209
 
210
- file_url = f"https://huggingface.co/{REPO}/resolve/main/{file_path}"
211
- stream_id = generate(file_url)
212
 
213
- if stream_id:
214
- return jsonify({'stream_id': stream_id})
 
 
 
 
 
 
 
 
215
 
216
- return "Streaming error", 500
217
-
218
- @app.route('/stream/<stream_id>')
219
- def stream_file(stream_id):
220
- stream_dir = os.path.join(CACHE_DIR, "stream", stream_id)
221
- playlist_path = os.path.join(stream_dir, 'output.m3u8')
222
-
223
- if os.path.exists(playlist_path):
224
- return Response(
225
- open(playlist_path, 'rb').read(),
226
- mimetype='application/vnd.apple.mpegurl'
227
- )
228
- return "Stream not found", 404
229
 
230
  if __name__ == '__main__':
231
  app.run(debug=True, host="0.0.0.0", port=7860)
 
1
+ from flask import Flask, jsonify, render_template, request, Response, abort
2
  import os
 
 
3
  import urllib.parse
4
+ from hf_scrapper import download_and_cache_file, get_system_proxies
 
 
 
5
  from dotenv import load_dotenv
6
+ import json
7
 
8
  load_dotenv()
9
  INDEX_FILE = os.getenv("INDEX_FILE")
 
11
  REPO = os.getenv("REPO")
12
  CACHE_DIR = os.getenv("CACHE_DIR")
13
 
14
+ # Ensure CACHE_DIR exists
15
  if not os.path.exists(CACHE_DIR):
16
  os.makedirs(CACHE_DIR)
17
 
 
 
18
  if not os.path.exists(INDEX_FILE):
19
  raise FileNotFoundError(f"{INDEX_FILE} not found. Please make sure the file exists.")
20
 
21
  with open(INDEX_FILE, 'r') as f:
22
  file_structure = json.load(f)
23
 
24
+ app = Flask(__name__)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
  def get_film_file_path(title):
27
  decoded_title = urllib.parse.unquote(title)
28
  normalized_title = decoded_title.split(' (')[0].strip()
29
+
30
  for item in file_structure:
31
  if item['path'].startswith('films'):
32
  for sub_item in item['contents']:
33
  sub_item_title = sub_item['path'].split('/')[-1]
34
  normalized_sub_item_title = sub_item_title.split(' (')[0].strip()
35
+
36
  if normalized_title == normalized_sub_item_title:
37
  for file in sub_item['contents']:
38
  if file['type'] == 'file':
 
58
  return seasons
59
  return []
60
 
61
+ def download_film_if_needed(file_path):
62
+ cached_file_path = os.path.join(CACHE_DIR, file_path)
63
+ if not os.path.exists(cached_file_path):
64
+ file_url = f"https://huggingface.co/{REPO}/resolve/main/{file_path}"
65
+ success = download_and_cache_file(file_url, TOKEN, cached_file_path, proxies=get_system_proxies())
66
+ if not success:
67
+ abort(404, description="File not found or failed to download.")
68
+ return cached_file_path
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
 
70
  @app.route('/')
71
  def home():
72
  return render_template('index.html')
73
 
 
 
 
 
 
 
 
 
 
 
 
74
  @app.route('/films')
75
  def films():
76
  return render_template('films.html')
 
146
  def film_details(title):
147
  return render_template('film_details_page.html', title=title)
148
 
149
+ @app.route('/film/<path:title>/play')
150
+ def film_player(title):
151
+ title = urllib.parse.unquote(title)
152
+ film_file_path = get_film_file_path(title)
153
+ if not film_file_path:
154
+ return jsonify({'error': 'Film not found'}), 404
155
 
156
+ cached_file_path = download_film_if_needed(film_file_path)
 
157
 
158
+ return render_template('film_player.html', title=title)
159
+
160
+ @app.route('/stream/<path:title>')
161
+ def stream_film(title):
162
+ title = urllib.parse.unquote(title)
163
+ film_file_path = get_film_file_path(title)
164
+ if not film_file_path:
165
+ return jsonify({'error': 'Film not found'}), 404
166
+
167
+ cached_file_path = download_film_if_needed(film_file_path)
168
 
169
+ def generate():
170
+ with open(cached_file_path, 'rb') as f:
171
+ while chunk := f.read(8192):
172
+ yield chunk
173
+
174
+ return Response(generate(), mimetype='video/mp4')
 
 
 
 
 
 
 
175
 
176
  if __name__ == '__main__':
177
  app.run(debug=True, host="0.0.0.0", port=7860)
hf_scrapper.py CHANGED
@@ -19,20 +19,23 @@ def get_system_proxies():
19
  return {}
20
 
21
 
22
- # def download_file(file_url, token, output_path, proxies):
23
- # print(f"Downloading file from URL: {file_url} with proxies: {proxies}")
24
- # try:
25
- # response = requests.get(file_url, headers={'Authorization': f'Bearer {token}'}, proxies=proxies, stream=True)
26
- # response.raise_for_status()
27
- # with open(output_path, 'wb') as f:
28
- # for chunk in response.iter_content(chunk_size=8192):
29
- # if chunk:
30
- # f.write(chunk)
31
- # print(f'File {output_path} downloaded successfully.')
32
- # except RequestException as e:
33
- # print(f"Error downloading file: {e}")
34
- # except IOError as e:
35
- # print(f"Error writing file {output_path}: {e}")
 
 
 
36
 
37
  def get_file_structure(repo, token, path="", proxies=None):
38
  api_url = f"https://huggingface.co/api/models/{repo}/tree/main/{path}"
@@ -53,3 +56,11 @@ def write_file_structure_to_json(file_structure, file_path):
53
  print(f'File structure written to {file_path}')
54
  except IOError as e:
55
  print(f"Error writing file structure to JSON: {e}")
 
 
 
 
 
 
 
 
 
19
  return {}
20
 
21
 
22
+ def download_and_cache_file(file_url, token, cache_path, proxies=None):
23
+ print(f"Downloading file from URL: {file_url} to {cache_path} with proxies: {proxies}")
24
+ try:
25
+ response = requests.get(file_url, headers={'Authorization': f'Bearer {token}'}, proxies=proxies, stream=True)
26
+ response.raise_for_status()
27
+ os.makedirs(os.path.dirname(cache_path), exist_ok=True)
28
+ with open(cache_path, 'wb') as f:
29
+ for chunk in response.iter_content(chunk_size=8192):
30
+ if chunk:
31
+ f.write(chunk)
32
+ print(f'File cached to {cache_path} successfully.')
33
+ return True
34
+ except RequestException as e:
35
+ print(f"Error downloading file: {e}")
36
+ except IOError as e:
37
+ print(f"Error writing file {cache_path}: {e}")
38
+ return False
39
 
40
  def get_file_structure(repo, token, path="", proxies=None):
41
  api_url = f"https://huggingface.co/api/models/{repo}/tree/main/{path}"
 
56
  print(f'File structure written to {file_path}')
57
  except IOError as e:
58
  print(f"Error writing file structure to JSON: {e}")
59
+
60
+
61
+ if __name__ == "__main__":
62
+ file_url="https://huggingface.co/Unicone-Studio/jellyfin_media/resolve/main/films/Funky%20Monkey%202004/Funky%20Monkey%20(2004)%20Web-dl%201080p.mp4"
63
+ token=os.getenv("TOKEN")
64
+ cache_path="tmp/cache/films/Funky%20Monkey%202004/Funky%20Monkey%20(2004)%20Web-dl%201080p.mp4"
65
+ proxy=get_system_proxies()
66
+ download_and_cache_file(file_url, token, cache_path, proxies=proxy)
templates/film_details_page.html CHANGED
@@ -172,7 +172,7 @@
172
  metadataContainer.appendChild(createMetadataElement('Release Date', metadata.first_air_time));
173
 
174
  const playButton = document.getElementById('play-button');
175
- playButton.href = `/film_player?title=${encodeURIComponent(title)}`;
176
  }
177
 
178
  const urlParams = new URLSearchParams(window.location.search);
 
172
  metadataContainer.appendChild(createMetadataElement('Release Date', metadata.first_air_time));
173
 
174
  const playButton = document.getElementById('play-button');
175
+
176
  }
177
 
178
  const urlParams = new URLSearchParams(window.location.search);
templates/film_player.html CHANGED
@@ -9,82 +9,33 @@
9
  font-family: Arial, sans-serif;
10
  margin: 0;
11
  padding: 0;
 
 
 
 
12
  background-color: #f0f0f0;
13
  }
14
- .container {
15
- max-width: 800px;
16
- margin: auto;
17
- padding: 20px;
18
- background-color: #fff;
19
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
20
  border-radius: 8px;
 
21
  }
22
  video {
23
  width: 100%;
24
  height: auto;
25
- border-radius: 8px;
26
- }
27
- .title {
28
- font-size: 24px;
29
- font-weight: bold;
30
- margin-bottom: 20px;
31
- }
32
- .error {
33
- color: red;
34
- font-size: 18px;
35
- margin-top: 20px;
36
  }
37
  </style>
38
  </head>
39
  <body>
40
- <div class="container">
41
- <div class="title" id="filmTitle">Film Title</div>
42
- <video id="videoPlayer" controls>
43
- <source id="videoSource" type="application/vnd.apple.mpegurl">
44
  Your browser does not support the video tag.
45
  </video>
46
- <div id="error" class="error"></div>
47
  </div>
48
-
49
- <script>
50
- document.addEventListener("DOMContentLoaded", function() {
51
- const queryParams = new URLSearchParams(window.location.search);
52
- const filmTitle = queryParams.get('title');
53
-
54
- if (!filmTitle) {
55
- document.getElementById('error').innerText = 'No film title provided.';
56
- return;
57
- }
58
-
59
- // Display the film title
60
- document.getElementById('filmTitle').innerText = decodeURIComponent(filmTitle);
61
-
62
- // Fetch film metadata and stream URL
63
- fetch(`/api/film/${encodeURIComponent(filmTitle)}`)
64
- .then(response => response.json())
65
- .then(data => {
66
- if (data.file_path) {
67
- return fetch(`/api/stream?path=${encodeURIComponent(data.file_path)}`);
68
- } else {
69
- throw new Error('File path not found.');
70
- }
71
- })
72
- .then(response => response.json())
73
- .then(data => {
74
- if (data.stream_id) {
75
- // Update video source with the stream URL
76
- const videoSource = document.getElementById('videoSource');
77
- videoSource.src = `/stream/${data.stream_id}`;
78
- console.log("streamID : "+data.stream_id)
79
- document.getElementById('videoPlayer').load();
80
- } else {
81
- throw new Error('Stream ID not found.');
82
- }
83
- })
84
- .catch(error => {
85
- document.getElementById('error').innerText = `Error: ${error.message}`;
86
- });
87
- });
88
- </script>
89
  </body>
90
  </html>
 
9
  font-family: Arial, sans-serif;
10
  margin: 0;
11
  padding: 0;
12
+ display: flex;
13
+ justify-content: center;
14
+ align-items: center;
15
+ height: 100vh;
16
  background-color: #f0f0f0;
17
  }
18
+ .video-container {
19
+ position: relative;
20
+ width: 80%;
21
+ max-width: 1200px;
22
+ background: #000;
 
23
  border-radius: 8px;
24
+ overflow: hidden;
25
  }
26
  video {
27
  width: 100%;
28
  height: auto;
29
+ display: block;
 
 
 
 
 
 
 
 
 
 
30
  }
31
  </style>
32
  </head>
33
  <body>
34
+ <div class="video-container">
35
+ <video controls>
36
+ <source src="{{ url_for('stream_film', title=title) }}" type="video/mp4">
 
37
  Your browser does not support the video tag.
38
  </video>
 
39
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  </body>
41
  </html>
video.py CHANGED
@@ -7,7 +7,7 @@ logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %
7
 
8
  def ffmpeg_stream(file_url, token, output_dir="tmp/cache/stream", stream_id=None):
9
  # Generate a unique directory for the stream
10
- stream_dir = os.path.join(output_dir, stream_id) if stream_id else os.path.join(output_dir, str(uuid.uuid4()))
11
 
12
  if not os.path.exists(stream_dir):
13
  os.makedirs(stream_dir)
 
7
 
8
  def ffmpeg_stream(file_url, token, output_dir="tmp/cache/stream", stream_id=None):
9
  # Generate a unique directory for the stream
10
+ stream_dir = os.path.join(output_dir, stream_id)
11
 
12
  if not os.path.exists(stream_dir):
13
  os.makedirs(stream_dir)