views.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. import datetime
  2. import pytz
  3. from django.db.models import Q
  4. from django.http import HttpResponse
  5. from django.shortcuts import render, redirect
  6. from apps.main.models import Playlist
  7. from django.contrib.auth.decorators import login_required # redirects user to settings.LOGIN_URL
  8. from allauth.socialaccount.models import SocialToken
  9. from django.views.decorators.http import require_POST
  10. from django.contrib import messages
  11. from django.template import Context, loader
  12. # Create your views here.
  13. @login_required
  14. def home(request):
  15. user_profile = request.user.profile
  16. user_playlists = user_profile.playlists.order_by("-num_of_accesses")
  17. watching = user_profile.playlists.filter(marked_as="watching").order_by("-num_of_accesses")
  18. #### FOR NEWLY JOINED USERS ######
  19. channel_found = True
  20. if user_profile.just_joined:
  21. if user_profile.import_in_progress:
  22. return render(request, "import_in_progress.html")
  23. else:
  24. if user_profile.access_token.strip() == "" or user_profile.refresh_token.strip() == "":
  25. user_social_token = SocialToken.objects.get(account__user=request.user)
  26. user_profile.access_token = user_social_token.token
  27. user_profile.refresh_token = user_social_token.token_secret
  28. user_profile.expires_at = user_social_token.expires_at
  29. request.user.save()
  30. user_profile.just_joined = False
  31. user_profile.save()
  32. return render(request, "home.html", {"import_successful": True})
  33. # if Playlist.objects.getUserYTChannelID(request.user) == -1: # user channel not found
  34. # channel_found = False
  35. # else:
  36. # Playlist.objects.initPlaylist(request.user, None) # get all playlists from user's YT channel
  37. # return render(request, "home.html", {"import_successful": True})
  38. ##################################
  39. if request.method == "POST":
  40. print(request.POST)
  41. if Playlist.objects.initPlaylist(request.user, request.POST['playlist-id'].strip()) == -1:
  42. print("No such playlist found.")
  43. playlist = []
  44. videos = []
  45. else:
  46. playlist = user_profile.playlists.get(playlist_id__exact=request.POST['playlist-id'].strip())
  47. videos = playlist.videos.all()
  48. else: # GET request
  49. videos = []
  50. playlist = []
  51. print("TESTING")
  52. return render(request, 'home.html', {"channel_found": channel_found,
  53. "playlist": playlist,
  54. "videos": videos,
  55. "user_playlists": user_playlists,
  56. "watching": watching})
  57. @login_required
  58. def view_video(request, playlist_id, video_id):
  59. video = request.user.profile.playlists.get(playlist_id=playlist_id).videos.get(video_id=video_id)
  60. print(video.name)
  61. return HttpResponse(loader.get_template("intercooler/video_details.html").render({"video": video}))
  62. @login_required
  63. def video_notes(request, playlist_id, video_id):
  64. video = request.user.profile.playlists.get(playlist_id=playlist_id).videos.get(video_id=video_id)
  65. if request.method == "POST":
  66. if 'video-notes-text-area' in request.POST:
  67. video.user_notes = request.POST['video-notes-text-area']
  68. video.save()
  69. return HttpResponse(loader.get_template("intercooler/messages.html").render(
  70. {"message_type": "success", "message_content": "Saved!"}))
  71. else:
  72. print("GET VIDEO NOTES")
  73. return HttpResponse(loader.get_template("intercooler/video_notes.html").render({"video": video,
  74. "playlist_id": playlist_id}))
  75. @login_required
  76. def view_playlist(request, playlist_id):
  77. user_profile = request.user.profile
  78. user_playlists = user_profile.playlists.all()
  79. # specific playlist requested
  80. playlist = user_profile.playlists.get(playlist_id__exact=playlist_id)
  81. playlist.num_of_accesses += 1
  82. playlist.save()
  83. videos = playlist.videos.all()
  84. if not playlist.has_playlist_changed:
  85. print("Checking if playlist changed...")
  86. result = Playlist.objects.checkIfPlaylistChangedOnYT(request.user, playlist_id)
  87. if result[0] == 1: # full scan was done (full scan is done for a playlist if a week has passed)
  88. deleted_videos, unavailable_videos, added_videos = result[1:]
  89. print("CHANGES", deleted_videos, unavailable_videos, added_videos)
  90. playlist_changed_text = ["The following modifications happened to this playlist on YouTube:"]
  91. if deleted_videos != 0 or unavailable_videos != 0 or added_videos != 0:
  92. if added_videos > 0:
  93. playlist_changed_text.append(f"{added_videos} new video(s) were added")
  94. if deleted_videos > 0:
  95. playlist_changed_text.append(f"{deleted_videos} video(s) were deleted")
  96. if unavailable_videos > 0:
  97. playlist_changed_text.append(f"{unavailable_videos} video(s) went private/unavailable")
  98. playlist.playlist_changed_text = "\n".join(playlist_changed_text)
  99. playlist.has_playlist_changed = True
  100. playlist.save()
  101. elif result[0] == -1: # playlist changed
  102. print("!!!Playlist changed")
  103. current_playlist_vid_count = playlist.video_count
  104. new_playlist_vid_count = result[1]
  105. print(current_playlist_vid_count)
  106. print(new_playlist_vid_count)
  107. if current_playlist_vid_count > new_playlist_vid_count:
  108. playlist.playlist_changed_text = f"Looks like {current_playlist_vid_count - new_playlist_vid_count} video(s) were deleted from this playlist on YouTube!"
  109. else:
  110. playlist.playlist_changed_text = f"Looks like {new_playlist_vid_count - current_playlist_vid_count} video(s) were added to this playlist on YouTube!"
  111. playlist.has_playlist_changed = True
  112. playlist.save()
  113. print(playlist.playlist_changed_text)
  114. return render(request, 'view_playlist.html', {"playlist": playlist,
  115. "videos": videos,
  116. "user_playlists": user_playlists})
  117. @login_required
  118. def all_playlists(request, playlist_type):
  119. """
  120. Possible playlist types for marked_as attribute: (saved in database like this)
  121. "none", "watching", "plan-to-watch"
  122. """
  123. playlist_type = playlist_type.lower()
  124. if playlist_type == "" or playlist_type == "all":
  125. playlists = request.user.profile.playlists.all()
  126. playlist_type_display = "All Playlists"
  127. elif playlist_type == "user-owned": # YT playlists owned by user
  128. playlists = request.user.profile.playlists.all().filter(is_user_owned=True)
  129. playlist_type_display = "Your YouTube Playlists"
  130. elif playlist_type == "imported": # YT playlists (public) owned by others
  131. playlists = request.user.profile.playlists.all().filter(is_user_owned=False)
  132. playlist_type_display = "Imported playlists"
  133. elif playlist_type == "favorites": # YT playlists (public) owned by others
  134. playlists = request.user.profile.playlists.all().filter(is_favorite=True)
  135. playlist_type_display = "Favorites"
  136. elif playlist_type.lower() in ["watching", "plan-to-watch"]:
  137. playlists = request.user.profile.playlists.filter(marked_as=playlist_type.lower())
  138. playlist_type_display = playlist_type.lower().replace("-", " ")
  139. elif playlist_type.lower() == "home": # displays cards of all playlist types
  140. return render(request, 'playlists_home.html')
  141. else:
  142. return redirect('home')
  143. return render(request, 'all_playlists.html', {"playlists": playlists,
  144. "playlist_type": playlist_type,
  145. "playlist_type_display": playlist_type_display})
  146. @login_required
  147. def order_playlist_by(request, playlist_id, order_by):
  148. playlist = request.user.profile.playlists.get(playlist_id=playlist_id)
  149. display_text = "" # what to display when requested order/filter has no videws
  150. if order_by == "popularity":
  151. videos = playlist.videos.order_by("-like_count")
  152. elif order_by == "date-published":
  153. videos = playlist.videos.order_by("-published_at")
  154. elif order_by == "views":
  155. videos = playlist.videos.order_by("-view_count")
  156. elif order_by == "has-cc":
  157. videos = playlist.videos.filter(has_cc=True)
  158. display_text = "No videos in this playlist have CC"
  159. elif order_by == "duration":
  160. videos = playlist.videos.order_by("-duration_in_seconds")
  161. elif order_by == 'new-updates':
  162. videos = []
  163. display_text = "No new updates! Note that deleted videos will not show up here."
  164. if playlist.has_new_updates:
  165. recently_updated_videos = playlist.videos.filter(video_details_modified=True)
  166. for video in recently_updated_videos:
  167. if video.video_details_modified_at + datetime.timedelta(hours=12) < datetime.datetime.now(
  168. pytz.utc): # expired
  169. video.video_details_modified = False
  170. video.save()
  171. if playlist.videos.filter(video_details_modified=True).count() == 0:
  172. playlist.has_new_updates = False
  173. playlist.save()
  174. else:
  175. videos = playlist.videos.filter(video_details_modified=True)
  176. else:
  177. return redirect('home')
  178. return HttpResponse(loader.get_template("intercooler/videos.html").render({"playlist": playlist,
  179. "videos": videos,
  180. "display_text": display_text}))
  181. @login_required
  182. def order_playlists_by(request, playlist_type, order_by):
  183. if playlist_type == "" or playlist_type.lower() == "all":
  184. playlists = request.user.profile.playlists.all().order_by(f"-{order_by.replace('-', '_')}")
  185. playlist_type = "All Playlists"
  186. elif playlist_type.lower() == "favorites":
  187. playlists = request.user.profile.playlists.filter(is_favorite=True).order_by(f"-{order_by.replace('-', '_')}")
  188. playlist_type = "Favorites"
  189. elif playlist_type.lower() == "watching":
  190. playlists = request.user.profile.playlists.filter(on_watch=True).order_by(f"-{order_by.replace('-', '_')}")
  191. playlist_type = "Watching"
  192. else:
  193. return redirect('home')
  194. return render(request, 'all_playlists.html', {"playlists": playlists, "playlist_type": playlist_type})
  195. @login_required
  196. def mark_playlist_as(request, playlist_id, mark_as):
  197. playlist = request.user.profile.playlists.get(playlist_id=playlist_id)
  198. marked_as_response = ""
  199. if mark_as in ["watching", "on-hold", "plan-to-watch"]:
  200. playlist.marked_as = mark_as
  201. playlist.save()
  202. marked_as_response = f'<span class="badge bg-success text-white" >{mark_as.replace("-", " ")}</span>'
  203. elif mark_as == "none":
  204. playlist.marked_as = mark_as
  205. playlist.save()
  206. else:
  207. return render('home')
  208. return HttpResponse(marked_as_response)
  209. @login_required
  210. def playlists_home(request):
  211. return render(request, 'playlists_home.html')
  212. @login_required
  213. @require_POST
  214. def delete_videos(request):
  215. print(request.POST)
  216. return HttpResponse("Worked!")
  217. @login_required
  218. @require_POST
  219. def search_playlists(request, playlist_type):
  220. print(request.POST) # prints <QueryDict: {'search': ['aa']}>
  221. search_query = request.POST["search"]
  222. if playlist_type == "all":
  223. try:
  224. playlists = request.user.profile.playlists.all().filter(name__startswith=search_query)
  225. except:
  226. playlists = request.user.profile.playlists.all()
  227. playlist_type_display = "All Playlists"
  228. elif playlist_type == "user-owned": # YT playlists owned by user
  229. try:
  230. playlists = request.user.profile.playlists.filter(Q(name__startswith=search_query) & Q(is_user_owned=True))
  231. except:
  232. playlists = request.user.profile.playlists.filter(is_user_owned=True)
  233. playlist_type_display = "Your YouTube Playlists"
  234. elif playlist_type == "imported": # YT playlists (public) owned by others
  235. try:
  236. playlists = request.user.profile.playlists.filter(Q(name__startswith=search_query) & Q(is_user_owned=False))
  237. except:
  238. playlists = request.user.profile.playlists.filter(is_user_owned=False)
  239. playlist_type_display = "Imported Playlists"
  240. elif playlist_type == "favorites": # YT playlists (public) owned by others
  241. try:
  242. playlists = request.user.profile.playlists.filter(Q(name__startswith=search_query) & Q(is_favorite=True))
  243. except:
  244. playlists = request.user.profile.playlists.filter(is_favorite=True)
  245. playlist_type_display = "Your Favorites"
  246. elif playlist_type in ["watching", "plan-to-watch"]:
  247. try:
  248. playlists = request.user.profile.playlists.filter(
  249. Q(name__startswith=search_query) & Q(marked_as=playlist_type))
  250. except:
  251. playlists = request.user.profile.playlists.all().filter(marked_as=playlist_type)
  252. playlist_type_display = playlist_type.replace("-", " ")
  253. return HttpResponse(loader.get_template("intercooler/playlists.html")
  254. .render({"playlists": playlists,
  255. "playlist_type_display": playlist_type_display,
  256. "playlist_type": playlist_type,
  257. "search_query": search_query}))
  258. #### MANAGE VIDEOS #####
  259. def mark_video_favortie(request, playlist_id, video_id):
  260. video = request.user.profile.playlists.get(playlist_id=playlist_id).videos.get(video_id=video_id)
  261. if video.is_favorite:
  262. video.is_favorite = False
  263. video.save()
  264. return HttpResponse('<i class="far fa-heart"></i>')
  265. else:
  266. video.is_favorite = True
  267. video.save()
  268. return HttpResponse('<i class="fas fa-heart"></i>')
  269. ###########
  270. @login_required
  271. @require_POST
  272. def search_UnTube(request):
  273. print(request.POST)
  274. search_query = request.POST["search"]
  275. all_playlists = request.user.profile.playlists.all()
  276. videos = []
  277. starts_with = False
  278. contains = False
  279. if request.POST['search-settings'] == 'starts-with':
  280. playlists = request.user.profile.playlists.filter(name__startswith=search_query) if search_query != "" else []
  281. if search_query != "":
  282. for playlist in all_playlists:
  283. pl_videos = playlist.videos.filter(name__startswith=search_query)
  284. if pl_videos.count() != 0:
  285. for v in pl_videos.all():
  286. videos.append(v)
  287. starts_with = True
  288. else:
  289. playlists = request.user.profile.playlists.filter(name__contains=search_query) if search_query != "" else []
  290. if search_query != "":
  291. for playlist in all_playlists:
  292. pl_videos = playlist.videos.filter(name__contains=search_query)
  293. if pl_videos.count() != 0:
  294. for v in pl_videos.all():
  295. videos.append(v)
  296. contains = True
  297. return HttpResponse(loader.get_template("intercooler/search_untube.html")
  298. .render({"playlists": playlists,
  299. "videos": videos,
  300. "videos_count": len(videos),
  301. "search_query": search_query,
  302. "starts_with": starts_with,
  303. "contains": contains}))
  304. @login_required
  305. def manage_playlists(request):
  306. return render(request, "manage_playlists.html")
  307. @login_required
  308. def manage_view_page(request, page):
  309. if page == "import":
  310. return HttpResponse(loader.get_template("intercooler/manage_playlists_import.html")
  311. .render(
  312. {"manage_playlists_import_textarea": request.user.profile.manage_playlists_import_textarea}))
  313. elif page == "create":
  314. return HttpResponse("<br><hr><br><h2>Working on this.</h2>")
  315. elif page == "untube":
  316. return HttpResponse("<br><hr><br><h2>Coming soon. Maybe.</h2>")
  317. else:
  318. return redirect('home')
  319. @login_required
  320. @require_POST
  321. def manage_save(request, what):
  322. if what == "manage_playlists_import_textarea":
  323. request.user.profile.manage_playlists_import_textarea = request.POST["import-playlist-textarea"]
  324. request.user.save()
  325. return HttpResponse("")
  326. @login_required
  327. @require_POST
  328. def manage_import_playlists(request):
  329. playlist_links = request.POST["import-playlist-textarea"].replace(",", "").split("\n")
  330. num_playlists_already_in_db = 0
  331. num_playlists_initialized_in_db = 0
  332. num_playlists_not_found = 0
  333. new_playlists = []
  334. old_playlists = []
  335. not_found_playlists = []
  336. for playlist_link in playlist_links:
  337. if playlist_link != "":
  338. pl_id = Playlist.objects.getPlaylistId(playlist_link)
  339. if pl_id is None:
  340. num_playlists_not_found += 1
  341. continue
  342. status = Playlist.objects.initPlaylist(request.user, pl_id)
  343. if status == -1 or status == -2:
  344. print("\nNo such playlist found:", pl_id)
  345. num_playlists_not_found += 1
  346. not_found_playlists.append(playlist_link)
  347. elif status == -3:
  348. num_playlists_already_in_db += 1
  349. playlist = request.user.profile.playlists.get(playlist_id__exact=pl_id)
  350. old_playlists.append(playlist)
  351. else:
  352. print(status)
  353. playlist = request.user.profile.playlists.get(playlist_id__exact=pl_id)
  354. new_playlists.append(playlist)
  355. num_playlists_initialized_in_db += 1
  356. request.user.profile.manage_playlists_import_textarea = ""
  357. request.user.save()
  358. return HttpResponse(loader.get_template("intercooler/manage_playlists_import_results.html")
  359. .render(
  360. {"new_playlists": new_playlists,
  361. "old_playlists": old_playlists,
  362. "not_found_playlists": not_found_playlists,
  363. "num_playlists_already_in_db": num_playlists_already_in_db,
  364. "num_playlists_initialized_in_db": num_playlists_initialized_in_db,
  365. "num_playlists_not_found": num_playlists_not_found
  366. }))
  367. @login_required
  368. def update_playlist(request, playlist_id):
  369. deleted_video_ids, unavailable_videos, added_videos = Playlist.objects.updatePlaylist(request.user, playlist_id)
  370. playlist = request.user.profile.playlists.get(playlist_id=playlist_id)
  371. playlist_changed_text = ["Updates:"]
  372. if len(added_videos) != 0:
  373. playlist_changed_text.append(f"{len(added_videos)} added")
  374. for video in added_videos:
  375. playlist_changed_text.append(f"--> {video.name}")
  376. if len(unavailable_videos) != 0:
  377. if len(playlist_changed_text) == 1:
  378. playlist_changed_text.append(f"{len(unavailable_videos)} went unavailable")
  379. else:
  380. playlist_changed_text.append(f"\n{len(unavailable_videos)} went unavailable")
  381. for video in unavailable_videos:
  382. playlist_changed_text.append(f"--> {video.name}")
  383. if len(deleted_video_ids) != 0:
  384. if len(playlist_changed_text) == 1:
  385. playlist_changed_text.append(f"{len(deleted_video_ids)} deleted")
  386. else:
  387. playlist_changed_text.append(f"\n{len(deleted_video_ids)} deleted")
  388. for video_id in deleted_video_ids:
  389. video = playlist.videos.get(video_id=video_id)
  390. playlist_changed_text.append(f"--> {video.name}")
  391. video.delete()
  392. if len(playlist_changed_text) == 1:
  393. playlist_changed_text = ["Successfully refreshed playlist! No new changes found!"]
  394. else:
  395. playlist_changed_text.append("\nTip: Sort By Updates to see what changed in this playlist.")
  396. return HttpResponse(loader.get_template("intercooler/updated_playlist.html")
  397. .render(
  398. {"playlist_changed_text": "\n".join(playlist_changed_text),
  399. "playlist": playlist,
  400. "videos": playlist.videos.all()}))