2
0

views.py 16 KB

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