2
0

views.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. import bleach
  2. from django.db.models import Q
  3. from django.shortcuts import render, redirect
  4. from django.contrib.auth import logout
  5. from django.views.decorators.http import require_POST
  6. from django.contrib.auth.decorators import login_required
  7. from allauth.socialaccount.models import SocialToken
  8. from django.http import HttpResponse
  9. from django.contrib.auth.models import User
  10. from django.contrib import messages
  11. from backend.main.models import Playlist
  12. from .models import Untube
  13. from django.template import loader
  14. # Create your views here.
  15. def index(request):
  16. if Untube.objects.all().count() == 0:
  17. untube = Untube.objects.create()
  18. untube.save()
  19. if not request.session.exists(request.session.session_key):
  20. request.session.create()
  21. request.session['liked_untube'] = False
  22. if request.user.is_anonymous:
  23. return render(request, 'index.html', {"likes": Untube.objects.all().first().page_likes,
  24. "users_joined": User.objects.all().count()})
  25. else:
  26. return redirect('home')
  27. def about(request):
  28. return render(request, 'about.html')
  29. @login_required
  30. def profile(request):
  31. user_playlists = request.user.playlists.all()
  32. watching = user_playlists.filter(marked_as="watching")
  33. total_num_playlists = user_playlists.count()
  34. statistics = {
  35. "public_x": 0,
  36. "private_x": 0,
  37. "favorites_x": 0,
  38. "watching_x": 0,
  39. "imported_x": 0
  40. }
  41. if total_num_playlists != 0:
  42. # x means percentage
  43. statistics["public_x"] = round(user_playlists.filter(is_private_on_yt=False).count() / total_num_playlists,
  44. 1) * 100
  45. statistics["private_x"] = round(user_playlists.filter(is_private_on_yt=True).count() / total_num_playlists,
  46. 1) * 100
  47. statistics["favorites_x"] = round(user_playlists.filter(is_favorite=True).count() / total_num_playlists,
  48. 1) * 100
  49. statistics["watching_x"] = round(user_playlists.filter(marked_as="watching").count() / total_num_playlists,
  50. 1) * 100
  51. statistics["imported_x"] = round(user_playlists.filter(is_user_owned=False).count() / total_num_playlists,
  52. 1) * 100
  53. return render(request, 'profile.html', {
  54. "total_num_playlists": total_num_playlists,
  55. "statistics": statistics,
  56. "watching": watching})
  57. @login_required
  58. def settings(request):
  59. return render(request, 'settings.html')
  60. @require_POST
  61. def update_settings(request):
  62. print(request.POST)
  63. user = request.user
  64. username_input = bleach.clean(request.POST['username'].strip())
  65. message_content = "Saved!"
  66. # message_type = "success"
  67. if username_input != user.username:
  68. if User.objects.filter(username__exact=username_input).count() != 0:
  69. # message_type = "danger"
  70. message_content = f"Username {username_input} already taken"
  71. messages.error(request, message_content)
  72. else:
  73. user.username = username_input
  74. # user.save()
  75. message_content = f"Username updated to {username_input}!"
  76. messages.success(request, message_content)
  77. if 'open search in new tab' in request.POST and user.profile.open_search_new_tab is False:
  78. user.profile.open_search_new_tab = True
  79. elif 'open search in new tab' not in request.POST and user.profile.open_search_new_tab is True:
  80. user.profile.open_search_new_tab = False
  81. if 'enable gradient bg' in request.POST and user.profile.enable_gradient_bg is False:
  82. user.profile.enable_gradient_bg = True
  83. elif 'enable gradient bg' not in request.POST and user.profile.enable_gradient_bg is True:
  84. user.profile.enable_gradient_bg = False
  85. if 'confirm before deleting' in request.POST and user.profile.confirm_before_deleting is False:
  86. user.profile.confirm_before_deleting = True
  87. elif 'confirm before deleting' not in request.POST and user.profile.confirm_before_deleting is True:
  88. user.profile.confirm_before_deleting = False
  89. if 'hide videos' in request.POST and user.profile.hide_unavailable_videos is False:
  90. user.profile.hide_unavailable_videos = True
  91. elif 'hide videos' not in request.POST and user.profile.hide_unavailable_videos is True:
  92. user.profile.hide_unavailable_videos = False
  93. user.save()
  94. if message_content == "Saved!":
  95. messages.success(request, message_content)
  96. return redirect('settings')
  97. @login_required
  98. def delete_account(request):
  99. request.user.playlists.all().delete()
  100. request.user.videos.all().delete()
  101. request.user.playlist_tags.all().delete()
  102. request.user.profile.delete()
  103. request.user.delete()
  104. request.session.flush()
  105. messages.success(request, "Account data deleted successfully.")
  106. return redirect('index')
  107. @login_required
  108. def log_out(request):
  109. request.session.flush() # delete all stored session keys
  110. logout(request) # log out authenticated user
  111. if "troll" in request.GET:
  112. print("TROLLED")
  113. messages.success(request, "Hee Hee")
  114. else:
  115. messages.success(request, "Successfully logged out. Hope to see you back again!")
  116. return redirect('/')
  117. def cancel_import(request):
  118. user_profile = request.user.profile
  119. if user_profile.access_token.strip() == "" or user_profile.refresh_token.strip() == "":
  120. user_social_token = SocialToken.objects.get(account__user=request.user)
  121. user_profile.access_token = user_social_token.token
  122. user_profile.refresh_token = user_social_token.token_secret
  123. user_profile.expires_at = user_social_token.expires_at
  124. # request.user.save()
  125. user_profile.imported_yt_playlists = False
  126. user_profile.show_import_page = False
  127. user_profile.save()
  128. return redirect('home')
  129. def import_user_yt_playlists(request):
  130. request.user.profile.show_import_page = True
  131. request.user.profile.save(update_fields=['show_import_page'])
  132. return render(request, 'import_in_progress.html')
  133. @login_required
  134. def start_import(request):
  135. """
  136. Initializes only the user's playlist data in the database. Returns the progress bar, which will
  137. keep calling continue_import
  138. :param request:
  139. :return:
  140. """
  141. user_profile = request.user.profile
  142. if user_profile.access_token.strip() == "" or user_profile.refresh_token.strip() == "":
  143. user_social_token = SocialToken.objects.get(account__user=request.user)
  144. user_profile.access_token = user_social_token.token
  145. user_profile.refresh_token = user_social_token.token_secret
  146. user_profile.expires_at = user_social_token.expires_at
  147. request.user.save()
  148. result = Playlist.objects.initializePlaylist(request.user)
  149. if result["status"] == -1:
  150. print("User has no YT channel")
  151. return HttpResponse(loader.get_template('intercooler/progress_bar.html').render(
  152. {
  153. "channel_found": False,
  154. "error_message": result["error_message"]
  155. }
  156. ))
  157. elif result["status"] == -2:
  158. user_profile.import_in_progress = False
  159. user_profile.imported_yt_playlists = True
  160. user_profile.show_import_page = True
  161. user_profile.save()
  162. print("User has no playlists on YT")
  163. if request.user.profile.yt_channel_id == "":
  164. Playlist.objects.getUserYTChannelID(request.user)
  165. Playlist.objects.initializePlaylist(request.user, "LL")
  166. return HttpResponse(loader.get_template('intercooler/progress_bar.html').render(
  167. {"total_playlists": 0,
  168. "playlists_imported": 0,
  169. "done": True,
  170. "progress": 100,
  171. "channel_found": True}))
  172. else:
  173. if request.user.profile.yt_channel_id == "":
  174. Playlist.objects.getUserYTChannelID(request.user)
  175. Playlist.objects.initializePlaylist(request.user, "LL")
  176. user_profile.import_in_progress = True
  177. user_profile.save()
  178. return HttpResponse(loader.get_template('intercooler/progress_bar.html').render(
  179. {"total_playlists": result["num_of_playlists"],
  180. "playlist_name": result["first_playlist_name"],
  181. "playlists_imported": 0,
  182. "progress": 0,
  183. "channel_found": True}
  184. ))
  185. @login_required
  186. def continue_import(request):
  187. if request.user.profile.import_in_progress is False:
  188. return redirect('home')
  189. num_of_playlists = request.user.playlists.filter(Q(is_user_owned=True)).exclude(playlist_id="LL").count()
  190. print("NUM OF PLAYLISTS", num_of_playlists)
  191. try:
  192. remaining_playlists = request.user.playlists.filter(Q(is_user_owned=True) & Q(is_in_db=False)).exclude(
  193. playlist_id="LL")
  194. print(remaining_playlists.count(), "REMAINING PLAYLISTS")
  195. playlists_imported = num_of_playlists - remaining_playlists.count() + 1
  196. playlist = remaining_playlists.order_by("created_at")[0]
  197. playlist_name = playlist.name
  198. playlist_id = playlist.playlist_id
  199. Playlist.objects.getAllVideosForPlaylist(request.user, playlist_id)
  200. except:
  201. print("NO REMAINING PLAYLISTS")
  202. playlist_id = -1
  203. if playlist_id != -1:
  204. return HttpResponse(loader.get_template('intercooler/progress_bar.html').render(
  205. {"total_playlists": num_of_playlists,
  206. "playlists_imported": playlists_imported,
  207. "playlist_name": playlist_name,
  208. "progress": round((playlists_imported / num_of_playlists) * 100, 1),
  209. "channel_found": True}))
  210. else:
  211. # request.user.profile.just_joined = False
  212. request.user.profile.import_in_progress = False
  213. request.user.profile.imported_yt_playlists = True
  214. request.user.profile.show_import_page = True # set back to true again so as to show users the welcome screen on 'home'
  215. request.user.save()
  216. user_pl_count = request.user.playlists.filter(Q(is_user_owned=True) & Q(is_in_db=True)).exclude(
  217. playlist_id="LL").count()
  218. return HttpResponse(loader.get_template('intercooler/progress_bar.html').render(
  219. {"total_playlists": user_pl_count,
  220. "playlists_imported": user_pl_count,
  221. "done": True,
  222. "progress": 100,
  223. "channel_found": True}))
  224. @login_required
  225. def user_playlists_updates(request, action):
  226. """
  227. Gets all user created playlist's ids from YouTube and checks them with the user playlists imported on UnTube.
  228. If any playlist id is on UnTube but not on YouTube, deletes the playlist from YouTube.
  229. If any new playlist id, imports it to UnTube
  230. """
  231. if action == 'check-for-updates':
  232. user_playlists_on_UnTube = request.user.playlists.filter(Q(is_user_owned=True) & Q(is_in_db=True)).exclude(
  233. playlist_id="LL")
  234. result = Playlist.objects.initializePlaylist(request.user)
  235. print(result)
  236. youtube_playlist_ids = result["playlist_ids"]
  237. untube_playlist_ids = []
  238. for playlist in user_playlists_on_UnTube:
  239. untube_playlist_ids.append(playlist.playlist_id)
  240. deleted_playlist_ids = []
  241. deleted_playlist_names = []
  242. for pl_id in untube_playlist_ids:
  243. if pl_id not in youtube_playlist_ids: # ie this playlist was deleted on youtube
  244. deleted_playlist_ids.append(pl_id)
  245. pl = request.user.playlists.get(playlist_id__exact=pl_id)
  246. deleted_playlist_names.append(f"{pl.name} (had {pl.video_count} videos)")
  247. pl.delete()
  248. if result["num_of_playlists"] == user_playlists_on_UnTube.count() and len(deleted_playlist_ids) == 0:
  249. print("No new updates")
  250. playlists = []
  251. else:
  252. playlists = request.user.playlists.filter(Q(is_user_owned=True) & Q(is_in_db=False)).exclude(
  253. playlist_id="LL")
  254. print(
  255. f"New updates found! {playlists.count()} newly added and {len(deleted_playlist_ids)} playlists deleted!")
  256. print(deleted_playlist_names)
  257. return HttpResponse(loader.get_template('intercooler/user_playlist_updates.html').render(
  258. {"playlists": playlists,
  259. "deleted_playlist_names": deleted_playlist_names}))
  260. elif action == 'init-update':
  261. unimported_playlists = request.user.playlists.filter(Q(is_user_owned=True) & Q(is_in_db=False)).exclude(
  262. playlist_id="LL").count()
  263. return HttpResponse(f"""
  264. <div hx-get="/updates/user-playlists/start-update" hx-trigger="load" hx-target="#user-pl-updates">
  265. <div class="alert alert-dismissible fade show" role="alert" style="background-color: cadetblue">
  266. <div class="d-flex justify-content-center mt-4 mb-3 ms-2" id="loading-sign" >
  267. <img src="/static/svg-loaders/spinning-circles.svg" width="40" height="40">
  268. <h5 class="mt-2 ms-2 text-black">Importing {unimported_playlists} new playlists into UnTube, please wait!</h5>
  269. </div>
  270. </div>
  271. </div>
  272. """)
  273. elif action == 'start-update':
  274. unimported_playlists = request.user.playlists.filter(Q(is_user_owned=True) & Q(is_in_db=False)).exclude(
  275. playlist_id="LL")
  276. for playlist in unimported_playlists:
  277. Playlist.objects.getAllVideosForPlaylist(request.user, playlist.playlist_id)
  278. return HttpResponse("""
  279. <div class="alert alert-success alert-dismissible fade show d-flex justify-content-center" role="alert">
  280. <h4 class="">Successfully imported new playlists into UnTube!</h4>
  281. <meta http-equiv="refresh" content="0;url=/home/#recent-playlists" />
  282. <meta http-equiv="refresh" content="2;url=/home/" />
  283. <button type="button" class="btn-close" data-bs-dismiss="alert" aria-la bel="Close"></button>
  284. </div>
  285. """)
  286. @login_required
  287. def get_user_liked_videos_playlist(request):
  288. if not request.user.playlists.filter(Q(playlist_id="LL") & Q(is_in_db=True)).exists():
  289. Playlist.objects.initializePlaylist(request.user, "LL")
  290. Playlist.objects.getAllVideosForPlaylist(request.user, "LL")
  291. messages.success(request, "Successfully imported your Liked Videos playlist!")
  292. return HttpResponse("""
  293. <script>
  294. window.location.reload();
  295. </script>
  296. """)
  297. ### FOR INDEX.HTML
  298. @require_POST
  299. def like_untube(request):
  300. untube = Untube.objects.all().first()
  301. untube.page_likes += 1
  302. untube.save()
  303. request.session['liked_untube'] = True
  304. request.session.save()
  305. return HttpResponse(f"""
  306. <a hx-post="/unlike-untube/" hx-swap="outerHTML" style="text-decoration: none; color: black">
  307. <i class="fas fa-heart" style="color: #d02e2e"></i> {untube.page_likes} likes (p.s glad you liked it!)
  308. </a>
  309. """)
  310. @require_POST
  311. def unlike_untube(request):
  312. untube = Untube.objects.all().first()
  313. untube.page_likes -= 1
  314. untube.save()
  315. request.session['liked_untube'] = False
  316. request.session.save()
  317. return HttpResponse(f"""
  318. <a hx-post="/like-untube/" hx-swap="outerHTML" style="text-decoration: none; color: black">
  319. <i class="fas fa-heart"></i> {untube.page_likes} likes (p.s :/)
  320. </a>
  321. """)