mirror of
https://github.com/instaloader/instaloader.git
synced 2026-10-19 15:12:16 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
57892f191a | ||
|
|
26e74bad6a | ||
|
|
27a239f07f | ||
|
|
bb25f03c7d | ||
|
|
c54342bb98 | ||
|
|
d2bebcd00c | ||
|
|
4bc0a94e12 | ||
|
|
06845b53fc | ||
|
|
e9cec0b261 | ||
|
|
ac4cd9f595 |
+7
File diff suppressed because one or more lines are too long
Vendored
+7
File diff suppressed because one or more lines are too long
Vendored
+75
@@ -0,0 +1,75 @@
|
|||||||
|
@import url(bootstrap-4.1.3.min.css);
|
||||||
|
a {
|
||||||
|
color: #008d06; }
|
||||||
|
a:hover, a.current, a.active {
|
||||||
|
color: #f48400; }
|
||||||
|
a.current, a.active {
|
||||||
|
font-weight: 500; }
|
||||||
|
|
||||||
|
.doc-sidebar a:hover, .modal-body a:hover {
|
||||||
|
text-decoration: none; }
|
||||||
|
|
||||||
|
code {
|
||||||
|
color: #212529; }
|
||||||
|
|
||||||
|
.highlight {
|
||||||
|
border-radius: 0.3em; }
|
||||||
|
.highlight pre {
|
||||||
|
padding: 0.7em;
|
||||||
|
color: #fff; }
|
||||||
|
.highlight .c1 {
|
||||||
|
color: #008d06; }
|
||||||
|
.highlight .k, .highlight .kn, .highlight .ow {
|
||||||
|
color: #f48400;
|
||||||
|
font-weight: bold; }
|
||||||
|
.highlight .nb {
|
||||||
|
color: #f48400; }
|
||||||
|
.highlight .s1, .highlight .s2, .highlight .mi {
|
||||||
|
color: #ff7efd; }
|
||||||
|
|
||||||
|
.doc-content {
|
||||||
|
padding-top: 0.5em;
|
||||||
|
top: 3.5625em;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
overflow-y: auto; }
|
||||||
|
|
||||||
|
.doc-sidebar {
|
||||||
|
position: fixed;
|
||||||
|
padding-top: 0.5em;
|
||||||
|
top: 3.5625em;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
overflow-y: auto; }
|
||||||
|
.doc-sidebar ul {
|
||||||
|
list-style-type: none;
|
||||||
|
padding-left: 1em; }
|
||||||
|
.doc-sidebar ul.current {
|
||||||
|
padding-left: 0; }
|
||||||
|
.doc-sidebar li {
|
||||||
|
padding-top: 0.2em;
|
||||||
|
padding-bottom: 0.2em; }
|
||||||
|
|
||||||
|
.admonition {
|
||||||
|
border-radius: 0.3em;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
background-color: #f8f9fa; }
|
||||||
|
.admonition .admonition-title {
|
||||||
|
color: #ff7efd; }
|
||||||
|
|
||||||
|
.topic {
|
||||||
|
border-radius: 0.3em;
|
||||||
|
background-color: #f8f9fa; }
|
||||||
|
|
||||||
|
.nav-link {
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 0; }
|
||||||
|
|
||||||
|
.btn-success {
|
||||||
|
background-color: #008d06;
|
||||||
|
border-color: #008d06; }
|
||||||
|
.btn-success:hover {
|
||||||
|
background-color: #f48400;
|
||||||
|
border-color: #f48400; }
|
||||||
|
|
||||||
|
/*# sourceMappingURL=instaloader.css.map */
|
||||||
Vendored
+22
@@ -0,0 +1,22 @@
|
|||||||
|
$(function () {
|
||||||
|
$('[data-toggle="tooltip"]').tooltip();
|
||||||
|
|
||||||
|
$('.doc-sidebar > ul > li.current').attr("id", "localtoc");
|
||||||
|
$('#localtoc ul').addClass("nav flex-column");
|
||||||
|
$('#localtoc ul li').addClass("nav-item");
|
||||||
|
$('#localtoc ul li a').addClass("nav-link");
|
||||||
|
$('.doc-content').scrollspy({target: '#localtoc'});
|
||||||
|
|
||||||
|
const top_href = '#' + $('.section:first').attr("id");
|
||||||
|
$('#localtoc > a.current').attr("href", top_href);
|
||||||
|
|
||||||
|
$('#navbarToc a').on("click", function () {
|
||||||
|
const href = $(this).attr("href");
|
||||||
|
if (href === '#') {
|
||||||
|
window.location.href = top_href;
|
||||||
|
} else {
|
||||||
|
window.location.href = href;
|
||||||
|
}
|
||||||
|
$('#navbarToc').modal('hide');
|
||||||
|
});
|
||||||
|
});
|
||||||
Vendored
+117
@@ -0,0 +1,117 @@
|
|||||||
|
@import 'bootstrap-4.1.3.min.css';
|
||||||
|
|
||||||
|
$color_instaloader_main: #008d06;
|
||||||
|
$color_instaloader_accent: #f48400;
|
||||||
|
$color_instaloader_hyper: #ff7efd;
|
||||||
|
$color_normal_text: #212529;
|
||||||
|
$color_light_bg: #f8f9fa;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: $color_instaloader_main;
|
||||||
|
|
||||||
|
&:hover, &.current, &.active {
|
||||||
|
color: $color_instaloader_accent;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.current, &.active {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.doc-sidebar, .modal-body {
|
||||||
|
a:hover {
|
||||||
|
text-decoration: none
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
color: $color_normal_text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight {
|
||||||
|
border-radius: 0.3em;
|
||||||
|
|
||||||
|
pre {
|
||||||
|
padding: 0.7em;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c1 {
|
||||||
|
color: $color_instaloader_main
|
||||||
|
}
|
||||||
|
|
||||||
|
.k, .kn, .ow {
|
||||||
|
color: $color_instaloader_accent;
|
||||||
|
font-weight: bold
|
||||||
|
}
|
||||||
|
|
||||||
|
.nb {
|
||||||
|
color: $color_instaloader_accent
|
||||||
|
}
|
||||||
|
|
||||||
|
.s1, .s2, .mi {
|
||||||
|
color: $color_instaloader_hyper
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.doc-content {
|
||||||
|
padding-top: 0.5em;
|
||||||
|
top: 3.5625em;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.doc-sidebar {
|
||||||
|
position: fixed;
|
||||||
|
padding-top: 0.5em;
|
||||||
|
top: 3.5625em;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style-type: none;
|
||||||
|
padding-left: 1em;
|
||||||
|
|
||||||
|
&.current {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
padding-top: 0.2em;
|
||||||
|
padding-bottom: 0.2em;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.admonition {
|
||||||
|
border-radius: 0.3em;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
background-color: $color_light_bg;
|
||||||
|
|
||||||
|
.admonition-title {
|
||||||
|
color: $color_instaloader_hyper;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.topic {
|
||||||
|
border-radius: 0.3em;
|
||||||
|
background-color: $color_light_bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-link {
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-success {
|
||||||
|
background-color: $color_instaloader_main;
|
||||||
|
border-color: $color_instaloader_main;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: $color_instaloader_accent;
|
||||||
|
border-color: $color_instaloader_accent;
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
-28
@@ -1,28 +0,0 @@
|
|||||||
code {
|
|
||||||
color: #222;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
dd {
|
|
||||||
padding-left: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
dl.option dd {
|
|
||||||
padding-left: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #008d06;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
color: #f48400;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.versionmodified {
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
Vendored
-14
@@ -1,14 +0,0 @@
|
|||||||
<li class="dropdown globaltoc-container">
|
|
||||||
<a role="button"
|
|
||||||
id="dLabelGlobalToc"
|
|
||||||
data-toggle="dropdown"
|
|
||||||
data-target="#"
|
|
||||||
href="{{ pathto(master_doc) }}">{{ theme_navbar_site_name }} <b class="caret"></b></a>
|
|
||||||
<ul class="dropdown-menu globaltoc"
|
|
||||||
role="menu"
|
|
||||||
aria-labelledby="dLabelGlobalToc">
|
|
||||||
{{ toctree(maxdepth=theme_globaltoc_depth|toint, collapse=False, includehidden=theme_globaltoc_includehidden|tobool) }}
|
|
||||||
<li role="separator" class="divider"></li>
|
|
||||||
<li class="toctree-l1"><a class="reference internal" href="genindex.html">Index</a></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
Vendored
+104
-7
@@ -1,18 +1,115 @@
|
|||||||
{% extends "!layout.html" %}
|
{% extends "!layout.html" %}
|
||||||
|
|
||||||
|
{% set html5_doctype = True %}
|
||||||
|
|
||||||
|
{% set script_files = script_files + ['_static/bootstrap-4.1.3.bundle.min.js', '_static/instaloader.js'] %}
|
||||||
|
|
||||||
{% block htmltitle %}
|
{% block htmltitle %}
|
||||||
{% if pagename == "index" %}
|
{% if pagename == "index" %}
|
||||||
<title>{{ title|striptags|e }}</title>
|
<title>{{ title|striptags|e }}</title>
|
||||||
{% else %}
|
{% else %}
|
||||||
<title>{{ title|striptags|e }}{{ titlesuffix }}</title>
|
<title>{{ title|striptags|e }}{{ titlesuffix }}</title>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block extrahead %}
|
{% block extrahead %}
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
<meta name="google-site-verification" content="FNUsDdkOJM9obm9QEdrNBVd9AtAzHzA-aaSMCUrS6C8" />
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<meta name="google-site-verification" content="FNUsDdkOJM9obm9QEdrNBVd9AtAzHzA-aaSMCUrS6C8"/>
|
||||||
{% if pagename == "index" %}
|
{% if pagename == "index" %}
|
||||||
<link rel="canonical" href="https://instaloader.github.io/" />
|
<link rel="canonical" href="https://instaloader.github.io/"/>
|
||||||
{% else %}
|
{% else %}
|
||||||
<link rel="canonical" href="https://instaloader.github.io/{{ pagename }}.html" />
|
<link rel="canonical" href="https://instaloader.github.io/{{ pagename }}.html"/>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<script async defer src="https://buttons.github.io/buttons.js"></script>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block header %}
|
||||||
|
<nav class="navbar navbar-expand-sm navbar-light bg-light fixed-top border-bottom">
|
||||||
|
<a class="navbar-brand" href="{{ pathto(master_doc) }}">
|
||||||
|
<img src="{{ pathto('_static/' + logo, 1) }}" width="30" height="30" class="d-inline-block align-top" alt="logo">
|
||||||
|
Instaloader
|
||||||
|
</a>
|
||||||
|
<ul class="navbar-nav mr-auto">
|
||||||
|
<li class="nav-item ml-0 ml-sm-2 ml-md-0">
|
||||||
|
<a target="_blank" class="nav-link"
|
||||||
|
href="https://github.com/instaloader/instaloader/releases/tag/v{{ current_release }}"
|
||||||
|
title="Released on {{ current_release_date }}" data-toggle="tooltip">v<b>{{ current_release }}</b></a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarLinks"
|
||||||
|
aria-controls="navbarLinks" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarLinks">
|
||||||
|
<ul class="navbar-nav ml-auto">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a target="_blank" class="nav-link" href="https://github.com/instaloader/instaloader/issues">Issues</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a target="_blank" class="nav-link" href="https://github.com/instaloader/instaloader/releases">Releases</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a target="_blank" class="nav-link" href="https://github.com/instaloader/instaloader">GitHub</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<form class="form-inline" action="search.html" method="get">
|
||||||
|
<div class="input-group my-2 my-sm-0 ml-2">
|
||||||
|
<input class="form-control" type="search" placeholder="Search" aria-label="Search" name="q">
|
||||||
|
<div class="input-group-append">
|
||||||
|
<button class="btn btn-success btn-small" type="submit">Go</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% if pagename != "index" %}
|
||||||
|
<button type="button" class="btn btn-success d-md-none btn-small ml-2" data-toggle="modal"
|
||||||
|
data-target="#navbarToc">
|
||||||
|
TOC
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
</nav>
|
||||||
|
{% if pagename != "index" %}
|
||||||
|
<div class="modal fade" id="navbarToc" tabindex="-1" role="dialog"
|
||||||
|
aria-hidden="true" aria-labelledby="navbarTocTitle">
|
||||||
|
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="navbarTocTitle">Contents</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
{{ toctree(maxdepth=-1) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block relbar1 %}{% endblock %}
|
||||||
|
{% block relbar2 %}{% endblock %}
|
||||||
|
{% block footer %}{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
<div class="container-fluid row">
|
||||||
|
<div class="col-12 {% if pagename != "index" %}col-md-9 col-xl-10{% endif %} doc-content">
|
||||||
|
{% block body %}{% endblock %}
|
||||||
|
</div>
|
||||||
|
{% if pagename != "index" %}
|
||||||
|
<div class="d-none col-md-3 col-xl-2 bg-light doc-sidebar d-md-flex flex-column border-left">
|
||||||
|
|
||||||
|
<h3>{{ _('Table of Contents') }}</h3>
|
||||||
|
{{ toctree(maxdepth=-1) }}
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between small mt-auto mb-2">
|
||||||
|
<a class="reference internal{% if pagename == "genindex" %} current{% endif %}" href="genindex.html">Index</a>
|
||||||
|
{% if pagename != "genindex" and pagename != "search" %}
|
||||||
|
<a class="reference external"
|
||||||
|
href="https://github.com/instaloader/instaloader/edit/master/docs/{{ pagename }}.rst">Edit this page</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
Vendored
-60
@@ -1,60 +0,0 @@
|
|||||||
<div id="navbar" class="{{ theme_navbar_class }} navbar-default {% if theme_navbar_fixed_top|tobool -%} navbar-fixed-top{%- endif -%}">
|
|
||||||
<div class="container">
|
|
||||||
<div class="navbar-header">
|
|
||||||
<!-- .btn-navbar is used as the toggle for collapsed navbar content -->
|
|
||||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".nav-collapse">
|
|
||||||
<span class="icon-bar"></span>
|
|
||||||
<span class="icon-bar"></span>
|
|
||||||
<span class="icon-bar"></span>
|
|
||||||
</button>
|
|
||||||
<a class="navbar-brand" href="{{ pathto(master_doc) }}">
|
|
||||||
{%- block sidebarlogo %}
|
|
||||||
{%- if logo %}<span><img src="{{ pathto('_static/' + logo, 1) }}"></span>{%- endif %}
|
|
||||||
{%- endblock %}
|
|
||||||
{% if theme_navbar_title -%}{{ theme_navbar_title|e }}{%- else -%}{{ project|e }}{%- endif -%}
|
|
||||||
</a>
|
|
||||||
<span class="navbar-text navbar-version pull-left">
|
|
||||||
<a href="https://github.com/instaloader/instaloader/releases/tag/v{{current_release}}" title="Released on {{current_release_date}}">v<b>{{current_release}}</b></a>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="collapse navbar-collapse nav-collapse">
|
|
||||||
<ul class="nav navbar-nav">
|
|
||||||
{% if theme_navbar_links %}
|
|
||||||
{%- for link in theme_navbar_links %}
|
|
||||||
<li><a href="{{ pathto(*link[1:]) }}">{{ link[0] }}</a></li>
|
|
||||||
{%- endfor %}
|
|
||||||
{% endif %}
|
|
||||||
{% block navbartoc %}
|
|
||||||
{% include "globaltoc.html" %}
|
|
||||||
{% if theme_navbar_pagenav and pagename not in ("index", "genindex", "installation") %}
|
|
||||||
{% include "navbartoc.html" %}
|
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
|
||||||
{% if theme_navbar_sidebarrel %}
|
|
||||||
{% block sidebarrel %}
|
|
||||||
{% include "relations.html" %}
|
|
||||||
{% endblock %}
|
|
||||||
{% endif %}
|
|
||||||
</ul>
|
|
||||||
<ul class="nav navbar-nav navbar-right">
|
|
||||||
<li class="dropdown">
|
|
||||||
<a role="button" id="dLabelUsefulLinks" data-toggle="dropdown" data-target="#" href="#">
|
|
||||||
Useful Links
|
|
||||||
<b class="caret"></b>
|
|
||||||
</a>
|
|
||||||
<ul class="dropdown-menu" role="menu" aria-labelledby="dLabelUsefulLinks">
|
|
||||||
<li><a href="https://github.com/instaloader/instaloader/issues">Issue Tracker</a></li>
|
|
||||||
<li><a href="https://github.com/instaloader/instaloader/releases">Version History</a></li>
|
|
||||||
<li><a href="https://pypi.org/project/instaloader/">Instaloader on PyPI</a></li>
|
|
||||||
<li><a href="https://github.com/instaloader/instaloader">Instaloader on GitHub</a></li>
|
|
||||||
<li role="separator" class="divider"></li>
|
|
||||||
<li style="padding-left: 20px;"><a class="github-button" href="https://github.com/instaloader/instaloader" data-icon="octicon-star" data-size="large" data-show-count="true" aria-label="Star Instaloader on GitHub">Star</a></li>
|
|
||||||
<li role="separator" class="divider"></li>
|
|
||||||
<li><a href="https://www.paypal.me/aandergr"><b>Donate (via Paypal)</b></a></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
Vendored
+28
@@ -0,0 +1,28 @@
|
|||||||
|
{% extends "!search.html" %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<div id="fallback" class="admonition warning">
|
||||||
|
<script type="text/javascript">$('#fallback').hide();</script>
|
||||||
|
<p>
|
||||||
|
{% trans %}Please activate JavaScript to enable the search
|
||||||
|
functionality.{% endtrans %}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{% if search_performed %}
|
||||||
|
<h1>{{ _('Search Results') }}</h1>
|
||||||
|
{% if not search_results %}
|
||||||
|
<p>{{ _('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.') }}</p>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
<div id="search-results">
|
||||||
|
{% if search_results %}
|
||||||
|
<ul>
|
||||||
|
{% for href, caption, context in search_results %}
|
||||||
|
<li><a href="{{ pathto(item.href) }}">{{ caption }}</a>
|
||||||
|
<div class="context">{{ context|e }}</div>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -249,18 +249,20 @@ Miscellaneous Options
|
|||||||
|
|
||||||
.. option:: +args.txt
|
.. option:: +args.txt
|
||||||
|
|
||||||
Read arguments from file `args.txt`, a shortcut to provide argument from
|
Read arguments from file `args.txt`, a shortcut to provide arguments from
|
||||||
file rather than command-line. This provide a convient way to hide login
|
file rather than command-line. This provides a convenient way to hide login
|
||||||
info from CLI. and also can use for simplify managment of long arguments.
|
info from CLI, and can also be used to simplify managment of long arguments.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
text file should separate arg with line break.
|
Text file should separate arguments with line breaks.
|
||||||
|
|
||||||
args.txt example::
|
args.txt example::
|
||||||
|
|
||||||
--login MYUSENAME
|
--login=MYUSERNAME
|
||||||
--password MYPASSWORD
|
--password=MYPASSWORD
|
||||||
--fast-update
|
--fast-update
|
||||||
|
profile1
|
||||||
|
profile2
|
||||||
|
|
||||||
.. versionadded:: 4.1
|
.. versionadded:: 4.1
|
||||||
|
|||||||
@@ -82,6 +82,15 @@ See also :class:`Post`, :meth:`Instaloader.download_post`,
|
|||||||
|
|
||||||
Discussed in :issue:`113`.
|
Discussed in :issue:`113`.
|
||||||
|
|
||||||
|
Top X Posts of User
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
With Instaloader, it is easy to download the few most-liked pictres of a user.
|
||||||
|
|
||||||
|
.. literalinclude:: codesnippets/194_top_x_of_user.py
|
||||||
|
|
||||||
|
Discussed in :issue:`194`.
|
||||||
|
|
||||||
Upgrade Images by Local Copies
|
Upgrade Images by Local Copies
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
from itertools import islice
|
||||||
|
from math import ceil
|
||||||
|
|
||||||
|
from instaloader import Instaloader, Profile
|
||||||
|
|
||||||
|
PROFILE = ... # profile to download from
|
||||||
|
X_percentage = 10 # percentage of posts that should be downloaded
|
||||||
|
|
||||||
|
L = Instaloader()
|
||||||
|
|
||||||
|
profile = Profile.from_username(L.context, PROFILE)
|
||||||
|
posts_sorted_by_likes = sorted(profile.get_posts(), key = lambda p: p.likes + p.comments)
|
||||||
|
|
||||||
|
for post in islice(posts_sorted_by_likes, ceil(profile.mediacount * X_percentage / 100)):
|
||||||
|
L.download_post(post, PROFILE)
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
from glob import glob
|
||||||
|
from os.path import expanduser
|
||||||
|
from sqlite3 import connect
|
||||||
|
|
||||||
|
from instaloader import ConnectionException, Instaloader
|
||||||
|
|
||||||
|
# FIREFOXCOOKIEFILE = "/home/alex/.mozilla/firefox/l96w6b90.default/cookies.sqlite"
|
||||||
|
FIREFOXCOOKIEFILE = glob(expanduser("~/.mozilla/firefox/*.default/cookies.sqlite"))[0]
|
||||||
|
|
||||||
|
instaloader = Instaloader(max_connection_attempts=1)
|
||||||
|
instaloader.context._session.cookies.update(connect(FIREFOXCOOKIEFILE)
|
||||||
|
.execute("SELECT name, value FROM moz_cookies "
|
||||||
|
"WHERE baseDomain='instagram.com'"))
|
||||||
|
|
||||||
|
try:
|
||||||
|
username = instaloader.test_login()
|
||||||
|
if not username:
|
||||||
|
raise ConnectionException()
|
||||||
|
except ConnectionException:
|
||||||
|
raise SystemExit("Cookie import failed. Are you logged in successfully in Firefox?")
|
||||||
|
|
||||||
|
instaloader.context.username = username
|
||||||
|
instaloader.save_session_to_file()
|
||||||
+3
-18
@@ -22,8 +22,6 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
import sphinx_bootstrap_theme
|
|
||||||
|
|
||||||
sys.path.insert(0, os.path.abspath('..'))
|
sys.path.insert(0, os.path.abspath('..'))
|
||||||
|
|
||||||
import docs.sphinx_autodoc_typehints as sphinx_autodoc_typehints
|
import docs.sphinx_autodoc_typehints as sphinx_autodoc_typehints
|
||||||
@@ -124,7 +122,7 @@ add_module_names = False
|
|||||||
# show_authors = False
|
# show_authors = False
|
||||||
|
|
||||||
# The name of the Pygments (syntax highlighting) style to use.
|
# The name of the Pygments (syntax highlighting) style to use.
|
||||||
pygments_style = 'sphinx'
|
pygments_style = 'fruity'
|
||||||
|
|
||||||
# A list of ignored prefixes for module index sorting.
|
# A list of ignored prefixes for module index sorting.
|
||||||
# modindex_common_prefix = []
|
# modindex_common_prefix = []
|
||||||
@@ -141,20 +139,7 @@ todo_include_todos = False
|
|||||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||||
# a list of builtin themes.
|
# a list of builtin themes.
|
||||||
#
|
#
|
||||||
html_theme = 'bootstrap'
|
html_theme = 'basic'
|
||||||
html_theme_path = sphinx_bootstrap_theme.get_html_theme_path()
|
|
||||||
|
|
||||||
# Theme options are theme-specific and customize the look and feel of a theme
|
|
||||||
# further. For a list of options available for each theme, see the
|
|
||||||
# documentation.
|
|
||||||
#
|
|
||||||
html_theme_options = {
|
|
||||||
'navbar_site_name': 'Site Contents',
|
|
||||||
'navbar_pagenav_name': 'Page Contents',
|
|
||||||
'navbar_pagenav': True,
|
|
||||||
'navbar_sidebarrel': True,
|
|
||||||
'nosidebar': True,
|
|
||||||
}
|
|
||||||
|
|
||||||
# Add any paths that contain custom themes here, relative to this directory.
|
# Add any paths that contain custom themes here, relative to this directory.
|
||||||
# html_theme_path = []
|
# html_theme_path = []
|
||||||
@@ -376,6 +361,6 @@ html_context = {'current_release': current_release, 'current_release_date': curr
|
|||||||
|
|
||||||
def setup(app):
|
def setup(app):
|
||||||
typing.TYPE_CHECKING = True
|
typing.TYPE_CHECKING = True
|
||||||
app.add_stylesheet("style.css")
|
app.add_stylesheet("instaloader.css")
|
||||||
app.connect('autodoc-process-signature', sphinx_autodoc_typehints.process_signature)
|
app.connect('autodoc-process-signature', sphinx_autodoc_typehints.process_signature)
|
||||||
app.connect('autodoc-process-docstring', sphinx_autodoc_typehints.process_docstring)
|
app.connect('autodoc-process-docstring', sphinx_autodoc_typehints.process_docstring)
|
||||||
|
|||||||
@@ -17,12 +17,14 @@ Reporting Bugs
|
|||||||
If you encounter a bug, do not hesitate to report it in our
|
If you encounter a bug, do not hesitate to report it in our
|
||||||
`Issue Tracker <https://github.com/instaloader/instaloader/issues>`__. Since
|
`Issue Tracker <https://github.com/instaloader/instaloader/issues>`__. Since
|
||||||
Instaloader is actively developed, the majority of bugs is fixed within only
|
Instaloader is actively developed, the majority of bugs is fixed within only
|
||||||
**3 days** after being reported. When reporting a problem, please keep the
|
**4 days** after being reported. When reporting a problem, please keep the
|
||||||
following in mind:
|
following in mind:
|
||||||
|
|
||||||
- Ensure you **use the latest version** of Instaloader. The currently-installed
|
- Ensure you **use the latest version** of Instaloader. The currently-installed
|
||||||
version can be found out with ``instaloader --version``.
|
version can be found out with ``instaloader --version``.
|
||||||
|
|
||||||
|
- Check whether there is a valid solution in our :ref:`troubleshooting` section.
|
||||||
|
|
||||||
- Briefly **check whether the bug has already been reported**. If you find an
|
- Briefly **check whether the bug has already been reported**. If you find an
|
||||||
issue reporting the same bug you encountered, comment there rather than
|
issue reporting the same bug you encountered, comment there rather than
|
||||||
opening a new issue. However, if unsure, please create a new issue.
|
opening a new issue. However, if unsure, please create a new issue.
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ Instaloader Documentation
|
|||||||
cli-options
|
cli-options
|
||||||
as-module
|
as-module
|
||||||
codesnippets
|
codesnippets
|
||||||
|
troubleshooting
|
||||||
contributing
|
contributing
|
||||||
|
|
||||||
Useful Links
|
Useful Links
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
requests
|
requests
|
||||||
sphinx!=1.7.7
|
sphinx!=1.7.7
|
||||||
sphinx-bootstrap-theme
|
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
.. _troubleshooting:
|
||||||
|
|
||||||
|
Troubleshooting
|
||||||
|
===============
|
||||||
|
|
||||||
|
.. highlight:: python
|
||||||
|
|
||||||
|
429 - Too Many Requests
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Instaloader has a logic to keep track of its requests to Instagram and to obey
|
||||||
|
their rate limits. Since they are nowhere documented, we try them out
|
||||||
|
experimentally. We have a daily cron job running to confirm that Instaloader
|
||||||
|
still stays within the rate limits. Nevertheless, the rate control logic assumes
|
||||||
|
that
|
||||||
|
|
||||||
|
- at one time, Instaloader is the only application that consumes requests. I.e.
|
||||||
|
neither the Instagram browser interface, nor a mobile app, nor another
|
||||||
|
Instaloader instance is running in parallel,
|
||||||
|
|
||||||
|
- no requests had been consumed when Instaloader starts.
|
||||||
|
|
||||||
|
The latter one implies that restarting or reinstantiating Instaloader often
|
||||||
|
within short time is prone to cause a 429. When a request is denied with a 429,
|
||||||
|
Instaloader retries the request as soon as the temporary ban is assumed to be
|
||||||
|
expired. In case the retry continuously fails for some reason, which should not
|
||||||
|
happen in normal conditions, consider adjusting the
|
||||||
|
:option:`--max-connection-attempts` option.
|
||||||
|
|
||||||
|
**"Too many queries in the last time"** is not an error. It is a notice that the
|
||||||
|
rate limit has almost been reached, according to Instaloader's own rate
|
||||||
|
accounting mechanism. We regularly adjust this mechanism to match Instagram's
|
||||||
|
current rate limiting.
|
||||||
|
|
||||||
|
Login error
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Instaloader's login *should* work fine, both with and without
|
||||||
|
Two-Factor-Authentication. It also supports handling the *checkpoint challenge*,
|
||||||
|
issued when Instagram suspects authentication activity on your account, by
|
||||||
|
pointing the user to an URL to be opened in a browser.
|
||||||
|
|
||||||
|
Nevertheless, in :issue:`92` users report problems with logging in. To still use
|
||||||
|
Instaloader's logged-in functionality, you may use the following script to
|
||||||
|
workaround login problems by importing the session cookies from Firefox and
|
||||||
|
bypassing Instaloader's login.
|
||||||
|
|
||||||
|
.. literalinclude:: codesnippets/92_import_firefox_session.py
|
||||||
|
|
||||||
|
To use this,
|
||||||
|
|
||||||
|
#. login to Instagram in Firefox,
|
||||||
|
|
||||||
|
#. execute the snippet,
|
||||||
|
|
||||||
|
#. then, ``instaloader -l USERNAME`` should work fine.
|
||||||
|
|
||||||
|
If you do not use your default firefox profile, or your operating system has the
|
||||||
|
paths differently set up, you may have to alter the ``FIREFOXCOOKIEFILE``
|
||||||
|
variable first.
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
"""Download pictures (or videos) along with their captions and other metadata from Instagram."""
|
"""Download pictures (or videos) along with their captions and other metadata from Instagram."""
|
||||||
|
|
||||||
|
|
||||||
__version__ = '4.2'
|
__version__ = '4.2.1'
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -179,6 +179,8 @@ def _main(instaloader: Instaloader, targetlist: List[str],
|
|||||||
if len(profiles) > 1:
|
if len(profiles) > 1:
|
||||||
instaloader.context.log("Downloading {} profiles: {}".format(len(profiles),
|
instaloader.context.log("Downloading {} profiles: {}".format(len(profiles),
|
||||||
' '.join([p.username for p in profiles])))
|
' '.join([p.username for p in profiles])))
|
||||||
|
if profiles and download_profile_pic and not instaloader.context.is_logged_in:
|
||||||
|
instaloader.context.error("Warning: Use --login to download HD version of profile pictures.")
|
||||||
instaloader.download_profiles(profiles,
|
instaloader.download_profiles(profiles,
|
||||||
download_profile_pic, download_posts, download_tagged, download_highlights,
|
download_profile_pic, download_posts, download_tagged, download_highlights,
|
||||||
download_stories, fast_update, post_filter, storyitem_filter)
|
download_stories, fast_update, post_filter, storyitem_filter)
|
||||||
|
|||||||
+23
-15
@@ -11,6 +11,7 @@ import tempfile
|
|||||||
from contextlib import contextmanager, suppress
|
from contextlib import contextmanager, suppress
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
from hashlib import md5
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from typing import Any, Callable, Iterator, List, Optional, Set, Union
|
from typing import Any, Callable, Iterator, List, Optional, Set, Union
|
||||||
|
|
||||||
@@ -254,8 +255,7 @@ class Instaloader:
|
|||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
def get_filename(index):
|
def get_filename(index):
|
||||||
return filename if index == 0 else (filename[:-4] + '_old_' +
|
return filename if index == 0 else '{0}_old_{2:02}{1}'.format(*os.path.splitext(filename), index)
|
||||||
(str(0) if index < 10 else str()) + str(index) + filename[-4:])
|
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
while os.path.isfile(get_filename(i)):
|
while os.path.isfile(get_filename(i)):
|
||||||
@@ -289,26 +289,34 @@ class Instaloader:
|
|||||||
"""Downloads and saves profile pic."""
|
"""Downloads and saves profile pic."""
|
||||||
|
|
||||||
def _epoch_to_string(epoch: datetime) -> str:
|
def _epoch_to_string(epoch: datetime) -> str:
|
||||||
return epoch.strftime('%Y-%m-%d_%H-%M-%S')
|
return epoch.strftime('%Y-%m-%d_%H-%M-%S_UTC')
|
||||||
|
|
||||||
profile_pic_url = profile.profile_pic_url
|
profile_pic_response = self.context.get_raw(profile.profile_pic_url)
|
||||||
with self.context.get_anonymous_session() as anonymous_session:
|
if 'Last-Modified' in profile_pic_response.headers:
|
||||||
date_object = datetime.strptime(anonymous_session.head(profile_pic_url).headers["Last-Modified"],
|
date_object = datetime.strptime(profile_pic_response.headers["Last-Modified"], '%a, %d %b %Y %H:%M:%S GMT')
|
||||||
'%a, %d %b %Y %H:%M:%S GMT')
|
profile_pic_bytes = None
|
||||||
|
profile_pic_identifier = _epoch_to_string(date_object)
|
||||||
|
else:
|
||||||
|
date_object = None
|
||||||
|
profile_pic_bytes = profile_pic_response.content
|
||||||
|
profile_pic_identifier = md5(profile_pic_bytes).hexdigest()[:16]
|
||||||
profile_pic_extension = 'jpg'
|
profile_pic_extension = 'jpg'
|
||||||
if ((format_string_contains_key(self.dirname_pattern, 'profile') or
|
if ((format_string_contains_key(self.dirname_pattern, 'profile') or
|
||||||
format_string_contains_key(self.dirname_pattern, 'target'))):
|
format_string_contains_key(self.dirname_pattern, 'target'))):
|
||||||
filename = '{0}/{1}_UTC_profile_pic.{2}'.format(self.dirname_pattern.format(profile=profile.username.lower(),
|
filename = '{0}/{1}_profile_pic.{2}'.format(self.dirname_pattern.format(profile=profile.username.lower(),
|
||||||
target=profile.username.lower()),
|
target=profile.username.lower()),
|
||||||
_epoch_to_string(date_object), profile_pic_extension)
|
profile_pic_identifier, profile_pic_extension)
|
||||||
else:
|
else:
|
||||||
filename = '{0}/{1}_{2}_UTC_profile_pic.{3}'.format(self.dirname_pattern.format(), profile.username.lower(),
|
filename = '{0}/{1}_{2}_profile_pic.{3}'.format(self.dirname_pattern.format(), profile.username.lower(),
|
||||||
_epoch_to_string(date_object), profile_pic_extension)
|
profile_pic_identifier, profile_pic_extension)
|
||||||
if os.path.isfile(filename):
|
content_length = profile_pic_response.headers.get('Content-Length', None)
|
||||||
|
if os.path.isfile(filename) and (not self.context.is_logged_in or
|
||||||
|
content_length is not None and os.path.getsize(filename) >= int(content_length)):
|
||||||
self.context.log(filename + ' already exists')
|
self.context.log(filename + ' already exists')
|
||||||
return None
|
return None
|
||||||
self.context.get_and_write_raw(profile_pic_url, filename)
|
self.context.write_raw(profile_pic_bytes if profile_pic_bytes else profile_pic_response, filename)
|
||||||
os.utime(filename, (datetime.now().timestamp(), date_object.timestamp()))
|
if date_object:
|
||||||
|
os.utime(filename, (datetime.now().timestamp(), date_object.timestamp()))
|
||||||
self.context.log('') # log output of _get_and_write_raw() does not produce \n
|
self.context.log('') # log output of _get_and_write_raw() does not produce \n
|
||||||
|
|
||||||
@_requires_login
|
@_requires_login
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import time
|
|||||||
import urllib.parse
|
import urllib.parse
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from typing import Any, Callable, Dict, Iterator, Optional
|
from typing import Any, Callable, Dict, Iterator, Optional, Union
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
import requests.utils
|
import requests.utils
|
||||||
@@ -429,8 +429,35 @@ class InstaloaderContext:
|
|||||||
data = _query()
|
data = _query()
|
||||||
yield from (edge['node'] for edge in data['edges'])
|
yield from (edge['node'] for edge in data['edges'])
|
||||||
|
|
||||||
def get_and_write_raw(self, url: str, filename: str, _attempt=1) -> None:
|
def get_iphone_json(self, path: str, params: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""Downloads raw data.
|
"""JSON request to ``i.instagram.com``.
|
||||||
|
|
||||||
|
:param path: URL, relative to ``i.instagram.com/``
|
||||||
|
:param params: GET parameters
|
||||||
|
:return: Decoded response dictionary
|
||||||
|
:raises QueryReturnedBadRequestException: When the server responds with a 400.
|
||||||
|
:raises QueryReturnedNotFoundException: When the server responds with a 404.
|
||||||
|
:raises ConnectionException: When query repeatedly failed.
|
||||||
|
|
||||||
|
.. versionadded:: 4.2.1"""
|
||||||
|
tempsession = copy_session(self._session)
|
||||||
|
tempsession.headers['User-Agent'] = 'Instagram 10.3.2 (iPhone7,2; iPhone OS 9_3_3; en_US; en-US; ' \
|
||||||
|
'scale=2.00; 750x1334) AppleWebKit/420+'
|
||||||
|
for header in ['Host', 'Origin', 'X-Instagram-AJAX', 'X-Requested-With']:
|
||||||
|
tempsession.headers.pop(header, None)
|
||||||
|
return self.get_json(path, params, 'i.instagram.com', tempsession)
|
||||||
|
|
||||||
|
def write_raw(self, resp: Union[bytes, requests.Response], filename: str) -> None:
|
||||||
|
"""Write raw response data into a file."""
|
||||||
|
self.log(filename, end=' ', flush=True)
|
||||||
|
with open(filename, 'wb') as file:
|
||||||
|
if isinstance(resp, requests.Response):
|
||||||
|
shutil.copyfileobj(resp.raw, file)
|
||||||
|
else:
|
||||||
|
file.write(resp)
|
||||||
|
|
||||||
|
def get_raw(self, url: str, _attempt=1) -> requests.Response:
|
||||||
|
"""Downloads a file anonymously.
|
||||||
|
|
||||||
:raises QueryReturnedNotFoundException: When the server responds with a 404.
|
:raises QueryReturnedNotFoundException: When the server responds with a 404.
|
||||||
:raises QueryReturnedForbiddenException: When the server responds with a 403.
|
:raises QueryReturnedForbiddenException: When the server responds with a 403.
|
||||||
@@ -439,10 +466,8 @@ class InstaloaderContext:
|
|||||||
with self.get_anonymous_session() as anonymous_session:
|
with self.get_anonymous_session() as anonymous_session:
|
||||||
resp = anonymous_session.get(url, stream=True)
|
resp = anonymous_session.get(url, stream=True)
|
||||||
if resp.status_code == 200:
|
if resp.status_code == 200:
|
||||||
self.log(filename, end=' ', flush=True)
|
resp.raw.decode_content = True
|
||||||
with open(filename, 'wb') as file:
|
return resp
|
||||||
resp.raw.decode_content = True
|
|
||||||
shutil.copyfileobj(resp.raw, file)
|
|
||||||
else:
|
else:
|
||||||
if resp.status_code == 403:
|
if resp.status_code == 403:
|
||||||
# suspected invalid URL signature
|
# suspected invalid URL signature
|
||||||
@@ -458,11 +483,19 @@ class InstaloaderContext:
|
|||||||
self.error(error_string + " [retrying; skip with ^C]", repeat_at_end=False)
|
self.error(error_string + " [retrying; skip with ^C]", repeat_at_end=False)
|
||||||
try:
|
try:
|
||||||
self._sleep()
|
self._sleep()
|
||||||
self.get_and_write_raw(url, filename, _attempt + 1)
|
return self.get_raw(url, _attempt + 1)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
self.error("[skipped by user]", repeat_at_end=False)
|
self.error("[skipped by user]", repeat_at_end=False)
|
||||||
raise ConnectionException(error_string) from err
|
raise ConnectionException(error_string) from err
|
||||||
|
|
||||||
|
def get_and_write_raw(self, url: str, filename: str) -> None:
|
||||||
|
"""Downloads and writes anonymously-requested raw data into a file.
|
||||||
|
|
||||||
|
:raises QueryReturnedNotFoundException: When the server responds with a 404.
|
||||||
|
:raises QueryReturnedForbiddenException: When the server responds with a 403.
|
||||||
|
:raises ConnectionException: When download repeatedly failed."""
|
||||||
|
self.write_raw(self.get_raw(url), filename)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def root_rhx_gis(self) -> Optional[str]:
|
def root_rhx_gis(self) -> Optional[str]:
|
||||||
"""rhx_gis string returned in the / query."""
|
"""rhx_gis string returned in the / query."""
|
||||||
|
|||||||
@@ -373,6 +373,10 @@ class Profile:
|
|||||||
self._has_public_story = None
|
self._has_public_story = None
|
||||||
self._node = node
|
self._node = node
|
||||||
self._rhx_gis = None
|
self._rhx_gis = None
|
||||||
|
self._iphone_struct_ = None
|
||||||
|
if 'iphone_struct' in node:
|
||||||
|
# if loaded from JSON with load_structure_from_file()
|
||||||
|
self._iphone_struct_ = node['iphone_struct']
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_username(cls, context: InstaloaderContext, username: str):
|
def from_username(cls, context: InstaloaderContext, username: str):
|
||||||
@@ -423,6 +427,8 @@ class Profile:
|
|||||||
json_node.pop('edge_media_collections', None)
|
json_node.pop('edge_media_collections', None)
|
||||||
json_node.pop('edge_owner_to_timeline_media', None)
|
json_node.pop('edge_owner_to_timeline_media', None)
|
||||||
json_node.pop('edge_saved_media', None)
|
json_node.pop('edge_saved_media', None)
|
||||||
|
if self._iphone_struct_:
|
||||||
|
json_node['iphone_struct'] = self._iphone_struct_
|
||||||
return json_node
|
return json_node
|
||||||
|
|
||||||
def _obtain_metadata(self):
|
def _obtain_metadata(self):
|
||||||
@@ -447,6 +453,15 @@ class Profile:
|
|||||||
d = d[key]
|
d = d[key]
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _iphone_struct(self) -> Dict[str, Any]:
|
||||||
|
if not self._context.is_logged_in:
|
||||||
|
raise LoginRequiredException("--login required to access iPhone profile info endpoint.")
|
||||||
|
if not self._iphone_struct_:
|
||||||
|
data = self._context.get_iphone_json(path='api/v1/users/{}/info/'.format(self.userid), params={})
|
||||||
|
self._iphone_struct_ = data['user']
|
||||||
|
return self._iphone_struct_
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def userid(self) -> int:
|
def userid(self) -> int:
|
||||||
"""User ID"""
|
"""User ID"""
|
||||||
@@ -563,10 +578,20 @@ class Profile:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def profile_pic_url(self) -> str:
|
def profile_pic_url(self) -> str:
|
||||||
"""Return URL of profile picture
|
"""Return URL of profile picture. If logged in, the HD version is returned, otherwise a lower-quality version.
|
||||||
|
|
||||||
.. versionadded:: 4.0.3"""
|
.. versionadded:: 4.0.3
|
||||||
return self._metadata("profile_pic_url_hd")
|
|
||||||
|
.. versionchanged:: 4.2.1
|
||||||
|
Require being logged in for HD version (as required by Instagram)."""
|
||||||
|
if self._context.is_logged_in:
|
||||||
|
try:
|
||||||
|
return self._iphone_struct['hd_profile_pic_url_info']['url']
|
||||||
|
except (InstaloaderException, KeyError) as err:
|
||||||
|
self._context.error('{} Unable to fetch high quality profile pic.'.format(err))
|
||||||
|
return self._metadata("profile_pic_url_hd")
|
||||||
|
else:
|
||||||
|
return self._metadata("profile_pic_url_hd")
|
||||||
|
|
||||||
def get_profile_pic_url(self) -> str:
|
def get_profile_pic_url(self) -> str:
|
||||||
""".. deprecated:: 4.0.3
|
""".. deprecated:: 4.0.3
|
||||||
|
|||||||
Reference in New Issue
Block a user